blob: ca3497ec13ee03eca65fbc65dc1788da4f968b87 [file] [log] [blame]
Armin Ronacherbd33f112008-04-18 09:17:32 +02001/**
2 * jinja2._speedups
3 * ~~~~~~~~~~~~~~~~
4 *
Armin Ronacher7ceced52008-05-03 10:15:31 +02005 * This module implements a few functions in C for better performance. It
6 * also defines a `tb_set_next` function that is used to patch the debug
7 * traceback. If the speedups module is not compiled a ctypes implementation
8 * is used.
Armin Ronacherbd33f112008-04-18 09:17:32 +02009 *
10 * :copyright: 2008 by Armin Ronacher.
11 * :license: BSD.
12 */
13
14#include <Python.h>
15
Armin Ronacherbd33f112008-04-18 09:17:32 +020016static PyObject* markup;
17
Mickaël Guérinc0d40d32008-05-05 17:08:51 +020018#define ESCAPED_CHARS_TABLE_SIZE 63
19
20static Py_ssize_t escaped_chars_delta_len[ESCAPED_CHARS_TABLE_SIZE];
21static Py_UNICODE *escaped_chars_repl[ESCAPED_CHARS_TABLE_SIZE];
Armin Ronacherbd33f112008-04-18 09:17:32 +020022
23static int
24init_constants(void)
25{
Mickaël Guérinc0d40d32008-05-05 17:08:51 +020026 memset(escaped_chars_delta_len, 0, sizeof (escaped_chars_delta_len));
27 /* memset(escaped_chars_repl, 0, sizeof (escaped_chars_repl)); */
28
29 escaped_chars_delta_len['"'] = 5;
30 escaped_chars_repl['"'] = ((PyUnicodeObject*)PyUnicode_DecodeASCII("&quot;", 6, NULL))->str;
31
32 escaped_chars_delta_len['&'] = 3;
33 escaped_chars_repl['&'] = ((PyUnicodeObject*)PyUnicode_DecodeASCII("&amp;", 5, NULL))->str;
34
35 escaped_chars_delta_len['<'] = 3;
36 escaped_chars_repl['<'] = ((PyUnicodeObject*)PyUnicode_DecodeASCII("&lt;", 4, NULL))->str;
37
38 escaped_chars_delta_len['>'] = 3;
39 escaped_chars_repl['>'] = ((PyUnicodeObject*)PyUnicode_DecodeASCII("&gt;", 4, NULL))->str;
Armin Ronacherbd33f112008-04-18 09:17:32 +020040
41 PyObject *module = PyImport_ImportModule("jinja2.utils");
42 if (!module)
43 return 0;
44 markup = PyObject_GetAttrString(module, "Markup");
45 Py_DECREF(module);
46
47 return 1;
48}
49
50static PyObject*
51escape_unicode(PyUnicodeObject *in)
52{
53 PyUnicodeObject *out;
Mickaël Guérinc0d40d32008-05-05 17:08:51 +020054 Py_UNICODE *inp = in->str;
55 const Py_UNICODE *inp_end = in->str + in->length;
56 Py_UNICODE *next_escp;
Armin Ronacherbd33f112008-04-18 09:17:32 +020057 Py_UNICODE *outp;
Mickaël Guérinc0d40d32008-05-05 17:08:51 +020058 Py_ssize_t delta=0, erepl=0, delta_len=0;
Armin Ronacherbd33f112008-04-18 09:17:32 +020059
60 /* First we need to figure out how long the escaped string will be */
Mickaël Guérinc0d40d32008-05-05 17:08:51 +020061 while (*(inp) || inp < inp_end) {
62 if (*inp < ESCAPED_CHARS_TABLE_SIZE && escaped_chars_delta_len[*inp]) {
63 delta += escaped_chars_delta_len[*inp];
Armin Ronacherf59bac22008-04-20 13:11:43 +020064 ++erepl;
Armin Ronacherbd33f112008-04-18 09:17:32 +020065 }
Mickaël Guérinc0d40d32008-05-05 17:08:51 +020066 ++inp;
67 }
Armin Ronacherbd33f112008-04-18 09:17:32 +020068
69 /* Do we need to escape anything at all? */
70 if (!erepl) {
71 Py_INCREF(in);
72 return (PyObject*)in;
73 }
74
Mickaël Guérinc0d40d32008-05-05 17:08:51 +020075 out = (PyUnicodeObject*)PyUnicode_FromUnicode(NULL, in->length + delta);
Armin Ronacherbd33f112008-04-18 09:17:32 +020076 if (!out)
77 return NULL;
78
79 outp = out->str;
80 inp = in->str;
Mickaël Guérinc0d40d32008-05-05 17:08:51 +020081 while (erepl-- > 0) {
82 /* look for the next sustitution */
83 next_escp = inp;
84 while (next_escp < inp_end) {
85 if (*next_escp < ESCAPED_CHARS_TABLE_SIZE && (delta_len = escaped_chars_delta_len[*next_escp])) {
86 ++delta_len;
87 break;
88 }
89 ++next_escp;
Armin Ronacherbd33f112008-04-18 09:17:32 +020090 }
Mickaël Guérinc0d40d32008-05-05 17:08:51 +020091
92 if (next_escp > inp) {
93 /* copy unescaped chars between inp and next_escp */
94 Py_UNICODE_COPY(outp, inp, next_escp-inp);
95 outp += next_escp-inp;
96 }
97
98 /* escape 'next_escp' */
99 Py_UNICODE_COPY(outp, escaped_chars_repl[*next_escp], delta_len);
100 outp += delta_len;
101
102 inp = next_escp + 1;
103 }
104 if (inp < inp_end) {
105 Py_UNICODE_COPY(outp, inp, in->length - (inp - in->str));
Armin Ronacherbd33f112008-04-18 09:17:32 +0200106 }
107
108 return (PyObject*)out;
109}
110
111
112static PyObject*
Armin Ronacherf59bac22008-04-20 13:11:43 +0200113soft_unicode(PyObject *self, PyObject *s)
Armin Ronacherbd33f112008-04-18 09:17:32 +0200114{
Armin Ronacherf59bac22008-04-20 13:11:43 +0200115 if (!PyUnicode_Check(s))
116 return PyObject_Unicode(s);
117 Py_INCREF(s);
118 return s;
119}
120
121
122static PyObject*
123escape(PyObject *self, PyObject *text)
124{
125 PyObject *s = NULL, *rv = NULL;
Armin Ronacherbd33f112008-04-18 09:17:32 +0200126
127 /* we don't have to escape integers, bools or floats */
128 if (PyInt_CheckExact(text) || PyLong_CheckExact(text) ||
129 PyFloat_CheckExact(text) || PyBool_Check(text) ||
Armin Ronacher7ceced52008-05-03 10:15:31 +0200130 text == Py_None)
131 return PyObject_CallFunctionObjArgs(markup, text, NULL);
Armin Ronacherbd33f112008-04-18 09:17:32 +0200132
133 /* if the object has an __html__ method that performs the escaping */
134 PyObject *html = PyObject_GetAttrString(text, "__html__");
135 if (html) {
136 rv = PyObject_CallObject(html, NULL);
137 Py_DECREF(html);
138 return rv;
139 }
140
141 /* otherwise make the object unicode if it isn't, then escape */
142 PyErr_Clear();
143 if (!PyUnicode_Check(text)) {
144 PyObject *unicode = PyObject_Unicode(text);
145 if (!unicode)
146 return NULL;
147 s = escape_unicode((PyUnicodeObject*)unicode);
148 Py_DECREF(unicode);
149 }
150 else
151 s = escape_unicode((PyUnicodeObject*)text);
152
153 /* convert the unicode string into a markup object. */
Armin Ronacher7ceced52008-05-03 10:15:31 +0200154 rv = PyObject_CallFunctionObjArgs(markup, (PyObject*)s, NULL);
155 Py_DECREF(s);
156 return rv;
Armin Ronacherbd33f112008-04-18 09:17:32 +0200157}
158
159
160static PyObject *
161tb_set_next(PyObject *self, PyObject *args)
162{
163 PyTracebackObject *tb, *old;
164 PyObject *next;
165
166 if (!PyArg_ParseTuple(args, "O!O:tb_set_next", &PyTraceBack_Type, &tb, &next))
167 return NULL;
168 if (next == Py_None)
169 next = NULL;
170 else if (!PyTraceBack_Check(next)) {
171 PyErr_SetString(PyExc_TypeError,
172 "tb_set_next arg 2 must be traceback or None");
173 return NULL;
174 }
175 else
176 Py_INCREF(next);
177
178 old = tb->tb_next;
179 tb->tb_next = (PyTracebackObject*)next;
180 Py_XDECREF(old);
181
182 Py_INCREF(Py_None);
183 return Py_None;
184}
185
186
187static PyMethodDef module_methods[] = {
Armin Ronacherf59bac22008-04-20 13:11:43 +0200188 {"escape", (PyCFunction)escape, METH_O,
Armin Ronacherbd33f112008-04-18 09:17:32 +0200189 "escape(s) -> string\n\n"
190 "Convert the characters &, <, >, and \" in string s to HTML-safe\n"
191 "sequences. Use this if you need to display text that might contain\n"
192 "such characters in HTML."},
Armin Ronacherf59bac22008-04-20 13:11:43 +0200193 {"soft_unicode", (PyCFunction)soft_unicode, METH_O,
194 "soft_unicode(object) -> string\n\n"
195 "Make a string unicode if it isn't already. That way a markup\n"
196 "string is not converted back to unicode."},
Armin Ronacherbd33f112008-04-18 09:17:32 +0200197 {"tb_set_next", (PyCFunction)tb_set_next, METH_VARARGS,
198 "Set the tb_next member of a traceback object."},
199 {NULL, NULL, 0, NULL} /* Sentinel */
200};
201
202
203#ifndef PyMODINIT_FUNC /* declarations for DLL import/export */
204#define PyMODINIT_FUNC void
205#endif
206PyMODINIT_FUNC
207init_speedups(void)
208{
209 if (!init_constants())
210 return;
211
212 Py_InitModule3("jinja2._speedups", module_methods, "");
213}