blob: 10ae6f692cd8d43b2f9789b1b2d52d5bb26542a0 [file] [log] [blame]
Armin Ronacherbd33f112008-04-18 09:17:32 +02001/**
2 * jinja2._speedups
3 * ~~~~~~~~~~~~~~~~
4 *
5 * This module implements a few functions in C for better performance.
6 *
7 * :copyright: 2008 by Armin Ronacher.
8 * :license: BSD.
9 */
10
11#include <Python.h>
12
13
Priit Laes4c81b162008-04-18 09:52:58 +020014static const char *samp = "&amp;", *slt = "&lt;", *sgt = "&gt;", *sqt = "&quot;";
Armin Ronacherbd33f112008-04-18 09:17:32 +020015static Py_UNICODE *amp, *lt, *gt, *qt;
16static PyObject* markup;
17
18
19static int
20init_constants(void)
21{
22 amp = ((PyUnicodeObject*)PyUnicode_DecodeASCII(samp, 5, NULL))->str;
23 lt = ((PyUnicodeObject*)PyUnicode_DecodeASCII(slt, 4, NULL))->str;
24 gt = ((PyUnicodeObject*)PyUnicode_DecodeASCII(sgt, 4, NULL))->str;
25 qt = ((PyUnicodeObject*)PyUnicode_DecodeASCII(sqt, 6, NULL))->str;
26
27 PyObject *module = PyImport_ImportModule("jinja2.utils");
28 if (!module)
29 return 0;
30 markup = PyObject_GetAttrString(module, "Markup");
31 Py_DECREF(module);
32
33 return 1;
34}
35
36static PyObject*
37escape_unicode(PyUnicodeObject *in)
38{
39 PyUnicodeObject *out;
40 Py_UNICODE *outp;
41
42 /* First we need to figure out how long the escaped string will be */
43 int len = 0, erepl = 0, repl = 0;
44 Py_UNICODE *inp = in->str;
45 while (*(inp) || in->length > inp - in->str)
46 switch (*inp++) {
47 case '&':
48 len += 5;
Armin Ronacherf59bac22008-04-20 13:11:43 +020049 ++erepl;
Armin Ronacherbd33f112008-04-18 09:17:32 +020050 break;
51 case '"':
52 len += 6;
Armin Ronacherf59bac22008-04-20 13:11:43 +020053 ++erepl;
Armin Ronacherbd33f112008-04-18 09:17:32 +020054 break;
55 case '<':
56 case '>':
57 len += 4;
Armin Ronacherf59bac22008-04-20 13:11:43 +020058 ++erepl;
Armin Ronacherbd33f112008-04-18 09:17:32 +020059 break;
60 default:
Armin Ronacherf59bac22008-04-20 13:11:43 +020061 ++len;
Armin Ronacherbd33f112008-04-18 09:17:32 +020062 }
63
64 /* Do we need to escape anything at all? */
65 if (!erepl) {
66 Py_INCREF(in);
67 return (PyObject*)in;
68 }
69
70 out = (PyUnicodeObject*)PyUnicode_FromUnicode(NULL, len);
71 if (!out)
72 return NULL;
73
74 outp = out->str;
75 inp = in->str;
76 while (*(inp) || in->length > inp - in->str) {
77 /* copy rest of string if we have replaced everything */
78 if (repl == erepl) {
79 Py_UNICODE_COPY(outp, inp, in->length - (inp - in->str));
80 break;
81 }
82 /* regular replacements */
83 switch (*inp) {
84 case '&':
85 Py_UNICODE_COPY(outp, amp, 5);
86 outp += 5;
Armin Ronacherf59bac22008-04-20 13:11:43 +020087 ++repl;
Armin Ronacherbd33f112008-04-18 09:17:32 +020088 break;
89 case '"':
90 Py_UNICODE_COPY(outp, qt, 6);
91 outp += 6;
Armin Ronacherf59bac22008-04-20 13:11:43 +020092 ++repl;
Armin Ronacherbd33f112008-04-18 09:17:32 +020093 break;
94 case '<':
95 Py_UNICODE_COPY(outp, lt, 4);
96 outp += 4;
Armin Ronacherf59bac22008-04-20 13:11:43 +020097 ++repl;
Armin Ronacherbd33f112008-04-18 09:17:32 +020098 break;
99 case '>':
100 Py_UNICODE_COPY(outp, gt, 4);
101 outp += 4;
Armin Ronacherf59bac22008-04-20 13:11:43 +0200102 ++repl;
Armin Ronacherbd33f112008-04-18 09:17:32 +0200103 break;
104 default:
105 *outp++ = *inp;
106 };
107 ++inp;
108 }
109
110 return (PyObject*)out;
111}
112
113
114static PyObject*
Armin Ronacherf59bac22008-04-20 13:11:43 +0200115soft_unicode(PyObject *self, PyObject *s)
Armin Ronacherbd33f112008-04-18 09:17:32 +0200116{
Armin Ronacherf59bac22008-04-20 13:11:43 +0200117 if (!PyUnicode_Check(s))
118 return PyObject_Unicode(s);
119 Py_INCREF(s);
120 return s;
121}
122
123
124static PyObject*
125escape(PyObject *self, PyObject *text)
126{
127 PyObject *s = NULL, *rv = NULL;
Armin Ronacherbd33f112008-04-18 09:17:32 +0200128
129 /* we don't have to escape integers, bools or floats */
130 if (PyInt_CheckExact(text) || PyLong_CheckExact(text) ||
131 PyFloat_CheckExact(text) || PyBool_Check(text) ||
132 text == Py_None) {
Armin Ronacherf59bac22008-04-20 13:11:43 +0200133 PyObject *args = PyTuple_New(1);
Armin Ronacherbd33f112008-04-18 09:17:32 +0200134 if (!args) {
135 Py_DECREF(s);
136 return NULL;
137 }
138 PyTuple_SET_ITEM(args, 0, text);
139 return PyObject_CallObject(markup, args);
140 }
141
142 /* if the object has an __html__ method that performs the escaping */
143 PyObject *html = PyObject_GetAttrString(text, "__html__");
144 if (html) {
145 rv = PyObject_CallObject(html, NULL);
146 Py_DECREF(html);
147 return rv;
148 }
149
150 /* otherwise make the object unicode if it isn't, then escape */
151 PyErr_Clear();
152 if (!PyUnicode_Check(text)) {
153 PyObject *unicode = PyObject_Unicode(text);
154 if (!unicode)
155 return NULL;
156 s = escape_unicode((PyUnicodeObject*)unicode);
157 Py_DECREF(unicode);
158 }
159 else
160 s = escape_unicode((PyUnicodeObject*)text);
161
162 /* convert the unicode string into a markup object. */
Armin Ronacherf59bac22008-04-20 13:11:43 +0200163 return PyObject_CallFunctionObjArgs(markup, (PyObject*)s, NULL);
Armin Ronacherbd33f112008-04-18 09:17:32 +0200164}
165
166
167static PyObject *
168tb_set_next(PyObject *self, PyObject *args)
169{
170 PyTracebackObject *tb, *old;
171 PyObject *next;
172
173 if (!PyArg_ParseTuple(args, "O!O:tb_set_next", &PyTraceBack_Type, &tb, &next))
174 return NULL;
175 if (next == Py_None)
176 next = NULL;
177 else if (!PyTraceBack_Check(next)) {
178 PyErr_SetString(PyExc_TypeError,
179 "tb_set_next arg 2 must be traceback or None");
180 return NULL;
181 }
182 else
183 Py_INCREF(next);
184
185 old = tb->tb_next;
186 tb->tb_next = (PyTracebackObject*)next;
187 Py_XDECREF(old);
188
189 Py_INCREF(Py_None);
190 return Py_None;
191}
192
193
194static PyMethodDef module_methods[] = {
Armin Ronacherf59bac22008-04-20 13:11:43 +0200195 {"escape", (PyCFunction)escape, METH_O,
Armin Ronacherbd33f112008-04-18 09:17:32 +0200196 "escape(s) -> string\n\n"
197 "Convert the characters &, <, >, and \" in string s to HTML-safe\n"
198 "sequences. Use this if you need to display text that might contain\n"
199 "such characters in HTML."},
Armin Ronacherf59bac22008-04-20 13:11:43 +0200200 {"soft_unicode", (PyCFunction)soft_unicode, METH_O,
201 "soft_unicode(object) -> string\n\n"
202 "Make a string unicode if it isn't already. That way a markup\n"
203 "string is not converted back to unicode."},
Armin Ronacherbd33f112008-04-18 09:17:32 +0200204 {"tb_set_next", (PyCFunction)tb_set_next, METH_VARARGS,
205 "Set the tb_next member of a traceback object."},
206 {NULL, NULL, 0, NULL} /* Sentinel */
207};
208
209
210#ifndef PyMODINIT_FUNC /* declarations for DLL import/export */
211#define PyMODINIT_FUNC void
212#endif
213PyMODINIT_FUNC
214init_speedups(void)
215{
216 if (!init_constants())
217 return;
218
219 Py_InitModule3("jinja2._speedups", module_methods, "");
220}