_speedups.c 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. /**
  2. * markupsafe._speedups
  3. * ~~~~~~~~~~~~~~~~~~~~
  4. *
  5. * This module implements functions for automatic escaping in C for better
  6. * performance.
  7. *
  8. * :copyright: (c) 2010 by Armin Ronacher.
  9. * :license: BSD.
  10. */
  11. #include <Python.h>
  12. #define ESCAPED_CHARS_TABLE_SIZE 63
  13. #define UNICHR(x) (PyUnicode_AS_UNICODE((PyUnicodeObject*)PyUnicode_DecodeASCII(x, strlen(x), NULL)));
  14. #if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN)
  15. typedef int Py_ssize_t;
  16. #define PY_SSIZE_T_MAX INT_MAX
  17. #define PY_SSIZE_T_MIN INT_MIN
  18. #endif
  19. static PyObject* markup;
  20. static Py_ssize_t escaped_chars_delta_len[ESCAPED_CHARS_TABLE_SIZE];
  21. static Py_UNICODE *escaped_chars_repl[ESCAPED_CHARS_TABLE_SIZE];
  22. static int
  23. init_constants(void)
  24. {
  25. PyObject *module;
  26. /* happing of characters to replace */
  27. escaped_chars_repl['"'] = UNICHR("&#34;");
  28. escaped_chars_repl['\''] = UNICHR("&#39;");
  29. escaped_chars_repl['&'] = UNICHR("&amp;");
  30. escaped_chars_repl['<'] = UNICHR("&lt;");
  31. escaped_chars_repl['>'] = UNICHR("&gt;");
  32. /* lengths of those characters when replaced - 1 */
  33. memset(escaped_chars_delta_len, 0, sizeof (escaped_chars_delta_len));
  34. escaped_chars_delta_len['"'] = escaped_chars_delta_len['\''] = \
  35. escaped_chars_delta_len['&'] = 4;
  36. escaped_chars_delta_len['<'] = escaped_chars_delta_len['>'] = 3;
  37. /* import markup type so that we can mark the return value */
  38. module = PyImport_ImportModule("markupsafe");
  39. if (!module)
  40. return 0;
  41. markup = PyObject_GetAttrString(module, "Markup");
  42. Py_DECREF(module);
  43. return 1;
  44. }
  45. static PyObject*
  46. escape_unicode(PyUnicodeObject *in)
  47. {
  48. PyUnicodeObject *out;
  49. Py_UNICODE *inp = PyUnicode_AS_UNICODE(in);
  50. const Py_UNICODE *inp_end = PyUnicode_AS_UNICODE(in) + PyUnicode_GET_SIZE(in);
  51. Py_UNICODE *next_escp;
  52. Py_UNICODE *outp;
  53. Py_ssize_t delta=0, erepl=0, delta_len=0;
  54. /* First we need to figure out how long the escaped string will be */
  55. while (*(inp) || inp < inp_end) {
  56. if (*inp < ESCAPED_CHARS_TABLE_SIZE) {
  57. delta += escaped_chars_delta_len[*inp];
  58. erepl += !!escaped_chars_delta_len[*inp];
  59. }
  60. ++inp;
  61. }
  62. /* Do we need to escape anything at all? */
  63. if (!erepl) {
  64. Py_INCREF(in);
  65. return (PyObject*)in;
  66. }
  67. out = (PyUnicodeObject*)PyUnicode_FromUnicode(NULL, PyUnicode_GET_SIZE(in) + delta);
  68. if (!out)
  69. return NULL;
  70. outp = PyUnicode_AS_UNICODE(out);
  71. inp = PyUnicode_AS_UNICODE(in);
  72. while (erepl-- > 0) {
  73. /* look for the next substitution */
  74. next_escp = inp;
  75. while (next_escp < inp_end) {
  76. if (*next_escp < ESCAPED_CHARS_TABLE_SIZE &&
  77. (delta_len = escaped_chars_delta_len[*next_escp])) {
  78. ++delta_len;
  79. break;
  80. }
  81. ++next_escp;
  82. }
  83. if (next_escp > inp) {
  84. /* copy unescaped chars between inp and next_escp */
  85. Py_UNICODE_COPY(outp, inp, next_escp-inp);
  86. outp += next_escp - inp;
  87. }
  88. /* escape 'next_escp' */
  89. Py_UNICODE_COPY(outp, escaped_chars_repl[*next_escp], delta_len);
  90. outp += delta_len;
  91. inp = next_escp + 1;
  92. }
  93. if (inp < inp_end)
  94. Py_UNICODE_COPY(outp, inp, PyUnicode_GET_SIZE(in) - (inp - PyUnicode_AS_UNICODE(in)));
  95. return (PyObject*)out;
  96. }
  97. static PyObject*
  98. escape(PyObject *self, PyObject *text)
  99. {
  100. PyObject *s = NULL, *rv = NULL, *html;
  101. /* we don't have to escape integers, bools or floats */
  102. if (PyLong_CheckExact(text) ||
  103. #if PY_MAJOR_VERSION < 3
  104. PyInt_CheckExact(text) ||
  105. #endif
  106. PyFloat_CheckExact(text) || PyBool_Check(text) ||
  107. text == Py_None)
  108. return PyObject_CallFunctionObjArgs(markup, text, NULL);
  109. /* if the object has an __html__ method that performs the escaping */
  110. html = PyObject_GetAttrString(text, "__html__");
  111. if (html) {
  112. rv = PyObject_CallObject(html, NULL);
  113. Py_DECREF(html);
  114. return rv;
  115. }
  116. /* otherwise make the object unicode if it isn't, then escape */
  117. PyErr_Clear();
  118. if (!PyUnicode_Check(text)) {
  119. #if PY_MAJOR_VERSION < 3
  120. PyObject *unicode = PyObject_Unicode(text);
  121. #else
  122. PyObject *unicode = PyObject_Str(text);
  123. #endif
  124. if (!unicode)
  125. return NULL;
  126. s = escape_unicode((PyUnicodeObject*)unicode);
  127. Py_DECREF(unicode);
  128. }
  129. else
  130. s = escape_unicode((PyUnicodeObject*)text);
  131. /* convert the unicode string into a markup object. */
  132. rv = PyObject_CallFunctionObjArgs(markup, (PyObject*)s, NULL);
  133. Py_DECREF(s);
  134. return rv;
  135. }
  136. static PyObject*
  137. escape_silent(PyObject *self, PyObject *text)
  138. {
  139. if (text != Py_None)
  140. return escape(self, text);
  141. return PyObject_CallFunctionObjArgs(markup, NULL);
  142. }
  143. static PyObject*
  144. soft_unicode(PyObject *self, PyObject *s)
  145. {
  146. if (!PyUnicode_Check(s))
  147. #if PY_MAJOR_VERSION < 3
  148. return PyObject_Unicode(s);
  149. #else
  150. return PyObject_Str(s);
  151. #endif
  152. Py_INCREF(s);
  153. return s;
  154. }
  155. static PyMethodDef module_methods[] = {
  156. {"escape", (PyCFunction)escape, METH_O,
  157. "escape(s) -> markup\n\n"
  158. "Convert the characters &, <, >, ', and \" in string s to HTML-safe\n"
  159. "sequences. Use this if you need to display text that might contain\n"
  160. "such characters in HTML. Marks return value as markup string."},
  161. {"escape_silent", (PyCFunction)escape_silent, METH_O,
  162. "escape_silent(s) -> markup\n\n"
  163. "Like escape but converts None to an empty string."},
  164. {"soft_unicode", (PyCFunction)soft_unicode, METH_O,
  165. "soft_unicode(object) -> string\n\n"
  166. "Make a string unicode if it isn't already. That way a markup\n"
  167. "string is not converted back to unicode."},
  168. {NULL, NULL, 0, NULL} /* Sentinel */
  169. };
  170. #if PY_MAJOR_VERSION < 3
  171. #ifndef PyMODINIT_FUNC /* declarations for DLL import/export */
  172. #define PyMODINIT_FUNC void
  173. #endif
  174. PyMODINIT_FUNC
  175. init_speedups(void)
  176. {
  177. if (!init_constants())
  178. return;
  179. Py_InitModule3("markupsafe._speedups", module_methods, "");
  180. }
  181. #else /* Python 3.x module initialization */
  182. static struct PyModuleDef module_definition = {
  183. PyModuleDef_HEAD_INIT,
  184. "markupsafe._speedups",
  185. NULL,
  186. -1,
  187. module_methods,
  188. NULL,
  189. NULL,
  190. NULL,
  191. NULL
  192. };
  193. PyMODINIT_FUNC
  194. PyInit__speedups(void)
  195. {
  196. if (!init_constants())
  197. return NULL;
  198. return PyModule_Create(&module_definition);
  199. }
  200. #endif