Implement TemplateIO in C. This gives a nice spee ... (quixote/src/_c_htmltext.c)

Neil Schemenauer <nascheme-fVcApmY9cLvQ3/1i3zOLAti2O/[email protected]> Wed, 08 Jan 2003 19:16:28 -0500
Newsgroups gmane.comp.web.quixote.cvs
Message-ID <[email protected]>
Update of /home/cvs/quixote/src
In directory hewson:/tmp/cvs-serv20578/src

Modified Files:
	_c_htmltext.c 
Log Message:
Implement TemplateIO in C.  This gives a nice speedup according to a
silly template intensive benchmark.  Real applications probably see a
few percent speed increase.

Use larger integer types in some places to prevent overflows.


Index: _c_htmltext.c
===================================================================
RCS file: /home/cvs/quixote/src/_c_htmltext.c,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -d -r1.2 -r1.3
--- _c_htmltext.c	8 Jan 2003 20:38:16 -0000	1.2
+++ _c_htmltext.c	9 Jan 2003 00:16:26 -0000	1.3
@@ -23,6 +23,18 @@
 
 #define QuoteWrapper_Check(v)	((v)->ob_type == &QuoteWrapper_Type)
 
+typedef struct {
+	PyObject_HEAD
+	int html;
+	char *buf;
+	size_t size;
+	size_t pos;
+} TemplateIO_Object;
+
+static PyTypeObject TemplateIO_Type;
+
+#define TemplateIO_Check(v)	((v)->ob_type == &TemplateIO_Type)
+
 
 static PyObject *
 type_error(const char *msg)
@@ -36,7 +48,7 @@
 {
 	PyObject *new_s;
 	char *ss, *new_ss;
-	int i, j, extra_space, size, new_size;
+	size_t i, j, extra_space, size, new_size;
 	if (!PyString_Check(s))
 		return type_error("str object required");
 	ss = PyString_AS_STRING(s);
@@ -290,7 +302,7 @@
 	if (args->ob_type->tp_as_mapping && !PyTuple_Check(args) &&
 	    !PyString_Check(args)) {
 		char *fmt = PyString_AS_STRING(htmltext_STR(self));
-		int i, n = PyString_GET_SIZE(htmltext_STR(self));
+		size_t i, n = PyString_GET_SIZE(htmltext_STR(self));
 		char last = 0;
 		/* second check necessary since '%s' % {} => '{}' */
 		for (i=0; i < n; i++) {
@@ -319,7 +331,7 @@
 		}
 	}
 	else if (PyTuple_Check(args)) {
-		int i, n = PyTuple_GET_SIZE(args);
+		long i, n = PyTuple_GET_SIZE(args);
 		wargs = PyTuple_New(n);
 		for (i=0; i < n; i++) {
 			PyObject *wvalue = wrap_arg(PyTuple_GET_ITEM(args, i));
@@ -387,7 +399,7 @@
 static PyObject *
 htmltext_join(PyObject *self, PyObject *args)
 {
-	int i;
+	long i;
 	PyObject *qargs, *rv;
 	if (!PySequence_Check(args)) {
 		return type_error("argument must be a sequence");
@@ -515,6 +527,145 @@
 							"capitalize", ""));
 }
 
+static PyObject *
+template_io_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+	TemplateIO_Object *self;
+	int html = 0;
+	static char *kwlist[] = {"html", 0};
+	if (!PyArg_ParseTupleAndKeywords(args, kwds, "|i:TemplateIO",
+					 kwlist, &html))
+		return NULL;
+	self = (TemplateIO_Object *)type->tp_alloc(type, 0);
+	if (self == NULL) {
+		return NULL;
+	}
+	self->html = html != 0;
+	self->buf = NULL;
+	self->size = 0;
+	self->pos = 0;
+	return (PyObject *)self;
+}
+
+static void
+template_io_dealloc(TemplateIO_Object *self)
+{
+	if (self->size > 0)
+		PyMem_Free(self->buf);
+	self->ob_type->tp_free((PyObject *)self);
+}
+
+static PyObject *
+template_io_str(TemplateIO_Object *self)
+{
+	return PyString_FromStringAndSize(self->buf, self->pos);
+}
+
+static PyObject *
+template_io_getvalue(TemplateIO_Object *self)
+{
+	if (self->html) {
+		return htmltext_from_string(template_io_str(self));
+	}
+	else {
+		return template_io_str(self);
+	}
+}
+
+static PyObject *
+template_io_repr(TemplateIO_Object *self)
+{
+	PyObject *s, *sr, *rv;
+	s = template_io_str(self);
+	if (s == NULL)
+		return NULL;
+	sr = PyObject_Repr(s);
+	Py_DECREF(s);
+	if (sr == NULL)
+		return NULL;
+	rv = PyString_FromFormat("<htmltext %s>", PyString_AsString(sr));
+	Py_DECREF(sr);
+	return rv;
+}
+
+
+static PyObject *
+template_io_do_concat(TemplateIO_Object *self, char *s, size_t size)
+{
+	/* note this adds a reference to self */
+	size_t i;
+	if (self->pos + size > self->size) {
+		size_t new_size;
+		char *new_buf;
+		if (self->size > size)
+			new_size = self->size * 2;
+		else
+			new_size = size * 2;
+		new_buf = PyMem_Realloc(self->buf, new_size);
+		if (new_buf == NULL)
+			return NULL;
+		self->buf = new_buf;
+		self->size = new_size;
+	}
+	for (i=0; i < size; i++) {
+		self->buf[self->pos] = s[i];
+		self->pos++;
+	}
+	assert (self->pos < self->size);
+	Py_INCREF(self);
+	return (PyObject *)self;
+}
+	
+
+static PyObject *
+template_io_iconcat(TemplateIO_Object *self, PyObject *other)
+{
+	PyObject *rv;
+	PyObject *s = NULL;
+	assert (TemplateIO_Check(self));
+	if (other == Py_None) {
+		Py_INCREF(self);
+		return (PyObject *)self;
+	}
+	else if (TemplateIO_Check(other)) {
+		TemplateIO_Object *o = (TemplateIO_Object *)other;
+		if (self->html && !o->html) {
+			PyObject *ss = PyString_FromStringAndSize(o->buf,
+								  o->pos);
+			if (ss == NULL)
+				return NULL;
+			s = escape_string(ss);
+			Py_DECREF(ss);
+			goto concat_str;
+		}
+		rv = template_io_do_concat(self, o->buf, o->pos);
+	}
+	else if (htmltextObject_Check(other)) {
+		PyStringObject *s = ((htmltextObject *)other)->s;
+		rv = template_io_do_concat(self,
+					   PyString_AS_STRING(s),
+					   PyString_GET_SIZE(s));
+	}
+	else {
+		if (self->html) {
+			PyObject *ss = PyObject_Str(other);
+			if (ss == NULL)
+				return NULL;
+			s = escape_string(ss);
+			Py_DECREF(ss);
+		} else {
+			s = PyObject_Str(other);
+		}
+concat_str:
+		if (s == NULL)
+			return NULL;
+		rv = template_io_do_concat(self, PyString_AS_STRING(s),
+					   PyString_GET_SIZE(s));
+		Py_XDECREF(s);
+	}
+	return rv;
+}
+
 static PyMethodDef htmltext_methods[] = {
 	{"join", (PyCFunction)htmltext_join, METH_O, ""},
 	{"startswith", (PyCFunction)htmltext_startswith, METH_O, ""},
@@ -656,6 +807,69 @@
 	0,			/*tp_call*/
 	(unaryfunc)quote_wrapper_str,  /*tp_str*/
 };
+
+static PySequenceMethods template_io_as_sequence = {
+	0,				/*sq_length*/
+	0,				/*sq_concat*/
+	0,				/*sq_repeat*/
+	0,				/*sq_item*/
+	0,				/*sq_slice*/
+	0,				/*sq_ass_item*/
+	0,				/*sq_ass_slice*/
+	0,				/*sq_contains*/
+	(binaryfunc)template_io_iconcat,/*sq_inplace_concat*/
+};
+
+static PyMethodDef template_io_methods[] = {
+	{"getvalue", (PyCFunction)template_io_getvalue, METH_NOARGS, ""},
+	{NULL, NULL}
+};
+
+static PyTypeObject TemplateIO_Type = {
+	PyObject_HEAD_INIT(&PyType_Type)
+	0,			/*ob_size*/
+	"TemplateIO",		/*tp_name*/
+	sizeof(TemplateIO_Object),/*tp_basicsize*/
+	0,			/*tp_itemsize*/
+	/* methods */
+	(destructor)template_io_dealloc, /*tp_dealloc*/
+	0,			/*tp_print*/
+	0,			/*tp_getattr*/
+	0,			/*tp_setattr*/
+	0,			/*tp_compare*/
+	(unaryfunc)template_io_repr,/*tp_repr*/
+	0,			/*tp_as_number*/
+	&template_io_as_sequence,/*tp_as_sequence*/
+	0,			/*tp_as_mapping*/
+	0,			/*tp_hash*/
+	0,			/*tp_call*/
+	(unaryfunc)template_io_str,/*tp_str*/
+	PyObject_GenericGetAttr,/*tp_getattro*/
+	0,			/*tp_setattro*/
+	0,			/*tp_as_buffer*/
+	Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+	0,			/*tp_doc*/
+	0,			/*tp_traverse*/
+	0,			/*tp_clear*/
+	0,			/*tp_richcompare*/
+	0,			/*tp_weaklistoffset*/
+	0,			/*tp_iter*/
+	0,			/*tp_iternext*/
+	template_io_methods,	/*tp_methods*/
+	0,			/*tp_members*/
+	0,			/*tp_getset*/
+	0,			/*tp_base*/
+	0,			/*tp_dict*/
+	0,			/*tp_descr_get*/
+	0,			/*tp_descr_set*/
+	0,			/*tp_dictoffset*/
+	0,			/*tp_init*/
+	PyType_GenericAlloc,	/*tp_alloc*/
+	template_io_new,	/*tp_new*/
+	_PyObject_Del,		/*tp_free*/
+	0,			/*tp_is_gc*/
+};
+
 /* --------------------------------------------------------------------- */
 
 static PyObject *
@@ -704,6 +918,8 @@
 	/* Initialize the type of the new type object here; doing it here
 	 * is required for portability to Windows without requiring C++. */
 	htmltext_Type.ob_type = &PyType_Type;
+	QuoteWrapper_Type.ob_type = &PyType_Type;
+	TemplateIO_Type.ob_type = &PyType_Type;
 
 	/* Create the module and add the functions */
 	m = Py_InitModule4("_c_htmltext", htmltext_module_methods, module_doc,
@@ -711,5 +927,7 @@
 
 	Py_INCREF((PyObject *)&htmltext_Type);
 	Py_INCREF((PyObject *)&QuoteWrapper_Type);
+	Py_INCREF((PyObject *)&TemplateIO_Type);
 	PyModule_AddObject(m, "htmltext", (PyObject *)&htmltext_Type);
+	PyModule_AddObject(m, "TemplateIO", (PyObject *)&TemplateIO_Type);
 }