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