Aluode/PerceptionLabPortable
0
1#include <Python.h>
2
3#define GET_DELTA(inp, inp_end, delta) \
4 while (inp < inp_end) { \
5 switch (*inp++) { \
6 case '"': \
7 case '\'': \
8 case '&': \
9 delta += 4; \
10 break; \
11 case '<': \
12 case '>': \
13 delta += 3; \
14 break; \
15 } \
16 }
17
18#define DO_ESCAPE(inp, inp_end, outp) \
19 { \
20 Py_ssize_t ncopy = 0; \
21 while (inp < inp_end) { \
22 switch (*inp) { \
23 case '"': \
24 memcpy(outp, inp-ncopy, sizeof(*outp)*ncopy); \
25 outp += ncopy; ncopy = 0; \
26 *outp++ = '&'; \
27 *outp++ = '#'; \
28 *outp++ = '3'; \
29 *outp++ = '4'; \
30 *outp++ = ';'; \
31 break; \
32 case '\'': \
33 memcpy(outp, inp-ncopy, sizeof(*outp)*ncopy); \
34 outp += ncopy; ncopy = 0; \
35 *outp++ = '&'; \
36 *outp++ = '#'; \
37 *outp++ = '3'; \
38 *outp++ = '9'; \
39 *outp++ = ';'; \
40 break; \
41 case '&': \
42 memcpy(outp, inp-ncopy, sizeof(*outp)*ncopy); \
43 outp += ncopy; ncopy = 0; \
44 *outp++ = '&'; \
45 *outp++ = 'a'; \
46 *outp++ = 'm'; \
47 *outp++ = 'p'; \
48 *outp++ = ';'; \
49 break; \
50 case '<': \
51 memcpy(outp, inp-ncopy, sizeof(*outp)*ncopy); \
52 outp += ncopy; ncopy = 0; \
53 *outp++ = '&'; \
54 *outp++ = 'l'; \
55 *outp++ = 't'; \
56 *outp++ = ';'; \
57 break; \
58 case '>': \
59 memcpy(outp, inp-ncopy, sizeof(*outp)*ncopy); \
60 outp += ncopy; ncopy = 0; \
61 *outp++ = '&'; \
62 *outp++ = 'g'; \
63 *outp++ = 't'; \
64 *outp++ = ';'; \
65 break; \
66 default: \
67 ncopy++; \
68 } \
69 inp++; \
70 } \
71 memcpy(outp, inp-ncopy, sizeof(*outp)*ncopy); \
72 }
73
74static PyObject*
75escape_unicode_kind1(PyUnicodeObject *in)
76{
77 Py_UCS1 *inp = PyUnicode_1BYTE_DATA(in);
78 Py_UCS1 *inp_end = inp + PyUnicode_GET_LENGTH(in);
79 Py_UCS1 *outp;
80 PyObject *out;
81 Py_ssize_t delta = 0;
82
83 GET_DELTA(inp, inp_end, delta);
84 if (!delta) {
85 Py_INCREF(in);
86 return (PyObject*)in;
87 }
88
89 out = PyUnicode_New(PyUnicode_GET_LENGTH(in) + delta,
90 PyUnicode_IS_ASCII(in) ? 127 : 255);
91 if (!out)
92 return NULL;
93
94 inp = PyUnicode_1BYTE_DATA(in);
95 outp = PyUnicode_1BYTE_DATA(out);
96 DO_ESCAPE(inp, inp_end, outp);
97 return out;
98}
99
100static PyObject*
101escape_unicode_kind2(PyUnicodeObject *in)
102{
103 Py_UCS2 *inp = PyUnicode_2BYTE_DATA(in);
104 Py_UCS2 *inp_end = inp + PyUnicode_GET_LENGTH(in);
105 Py_UCS2 *outp;
106 PyObject *out;
107 Py_ssize_t delta = 0;
108
109 GET_DELTA(inp, inp_end, delta);
110 if (!delta) {
111 Py_INCREF(in);
112 return (PyObject*)in;
113 }
114
115 out = PyUnicode_New(PyUnicode_GET_LENGTH(in) + delta, 65535);
116 if (!out)
117 return NULL;
118
119 inp = PyUnicode_2BYTE_DATA(in);
120 outp = PyUnicode_2BYTE_DATA(out);
121 DO_ESCAPE(inp, inp_end, outp);
122 return out;
123}
124
125
126static PyObject*
127escape_unicode_kind4(PyUnicodeObject *in)
128{
129 Py_UCS4 *inp = PyUnicode_4BYTE_DATA(in);
130 Py_UCS4 *inp_end = inp + PyUnicode_GET_LENGTH(in);
131 Py_UCS4 *outp;
132 PyObject *out;
133 Py_ssize_t delta = 0;
134
135 GET_DELTA(inp, inp_end, delta);
136 if (!delta) {
137 Py_INCREF(in);
138 return (PyObject*)in;
139 }
140
141 out = PyUnicode_New(PyUnicode_GET_LENGTH(in) + delta, 1114111);
142 if (!out)
143 return NULL;
144
145 inp = PyUnicode_4BYTE_DATA(in);
146 outp = PyUnicode_4BYTE_DATA(out);
147 DO_ESCAPE(inp, inp_end, outp);
148 return out;
149}
150
151static PyObject*
152escape_unicode(PyObject *self, PyObject *s)
153{
154 if (!PyUnicode_Check(s))
155 return NULL;
156
157 // This check is no longer needed in Python 3.12.
158 if (PyUnicode_READY(s))
159 return NULL;
160
161 switch (PyUnicode_KIND(s)) {
162 case PyUnicode_1BYTE_KIND:
163 return escape_unicode_kind1((PyUnicodeObject*) s);
164 case PyUnicode_2BYTE_KIND:
165 return escape_unicode_kind2((PyUnicodeObject*) s);
166 case PyUnicode_4BYTE_KIND:
167 return escape_unicode_kind4((PyUnicodeObject*) s);
168 }
169 assert(0); /* shouldn't happen */
170 return NULL;
171}
172
173static PyMethodDef module_methods[] = {
174 {"_escape_inner", (PyCFunction)escape_unicode, METH_O, NULL},
175 {NULL, NULL, 0, NULL} /* Sentinel */
176};
177
178static PyModuleDef_Slot module_slots[] = {
179#ifdef Py_mod_multiple_interpreters // Python 3.12+
180 {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
181#endif
182#ifdef Py_mod_gil // Python 3.13+
183 {Py_mod_gil, Py_MOD_GIL_NOT_USED},
184#endif
185 {0, NULL} /* Sentinel */
186};
187
188static struct PyModuleDef module_definition = {
189 .m_base = PyModuleDef_HEAD_INIT,
190 .m_name = "markupsafe._speedups",
191 .m_size = 0,
192 .m_methods = module_methods,
193 .m_slots = module_slots,
194};
195
196PyMODINIT_FUNC
197PyInit__speedups(void)
198{
199 return PyModuleDef_Init(&module_definition);
200}
201 