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