Add benchmark code
diff --git a/bench/.gitignore b/bench/.gitignore
new file mode 100644
index 0000000..4d8fd6b
--- /dev/null
+++ b/bench/.gitignore
@@ -0,0 +1,5 @@
+*
+!.gitignore
+!Makefile
+!bench.c
+!runbench.sh
diff --git a/bench/Makefile b/bench/Makefile
new file mode 100644
index 0000000..de5cf42
--- /dev/null
+++ b/bench/Makefile
@@ -0,0 +1,132 @@
+# The tests are compiled and linked using musl-cross. The advantage of this is
+# that everything is self-contained and thus easy to reproduce. Musl is also
+# designed for static linking with little overhead, which gives us a good
+# metric for the "complete package" of a statically linked minimal XML reader
+# for each lib.
+
+FILE1=enwiki-20130805-abstract5.xml
+FILE1URL=http://dumps.wikimedia.org/enwiki/20130805/enwiki-20130805-abstract5.xml
+FILE2=discogs_20130801_labels.xml
+FILE2URL=http://www.discogs.com/data/${FILE2}.gz
+FILES=${FILE1} ${FILE2}
+MUSLCROSS=x86_64-linux-musl
+MUSLCROSSURL=https://bitbucket.org/GregorR/musl-cross/downloads/crossx86-x86_64-linux-musl-0.9.11.tar.xz
+EXPAT=expat-2.1.0
+EXPATURL=http://downloads.sourceforge.net/project/expat/expat/2.1.0/${EXPAT}.tar.gz
+LIBXML2=libxml2-2.9.1
+LIBXML2URL=ftp://xmlsoft.org/libxml2/${LIBXML2}.tar.gz
+MXML=mxml-2.7
+MXMLURL=http://www.msweet.org/files/project3/${MXML}.tar.gz
+
+HOST=x86_64-musl-linux
+CC=${HOST}-gcc
+AR=${HOST}-ar
+STRIP=${HOST}-strip
+export CFLAGS = -static -O2
+export PATH := ${PATH}:${PWD}/${MUSLCROSS}/bin
+
+.PHONY: notice all clean distclean
+
+notice:
+	@echo "You probably don't want to 'make' these benchmarks."
+	@echo "Lots of stuff is downloaded, lots of stuff is compiled and lots of stuff is run."
+	@echo "Run 'make all -j1' if you do have the patience to run the benchmarks."
+	@echo
+	@false
+
+build: strlen yxml expat libxml2 mxml
+all: strlen-bench yxml-bench expat-bench libxml2-bench mxml-bench
+
+${FILE1}:
+	curl ${FILE1URL} -O ${FILE1}
+
+${FILE2}:
+	@echo "This doesn't work. Probably want to download the file manually with a browser"
+	curl ${FILE2URL} | zcat > ${FILE2}
+
+${MUSLCROSS}:
+	curl -L ${MUSLCROSSURL} | xzcat | tar -xvf-
+
+${EXPAT}:
+	curl -L ${EXPATURL} | zcat | tar -xvf-
+
+${LIBXML2}:
+	curl ${LIBXML2URL} | zcat | tar -xvf-
+
+${MXML}:
+	curl ${MXMLURL} | zcat | tar -xvf-
+
+
+
+yxml.o: ../yxml.c
+	make -C .. yxml.c
+	${CC} ${CFLAGS} -I.. ../yxml.c -c
+
+yxml: ${MUSLCROSS} bench.c yxml.o
+	${CC} ${CFLAGS} -I.. -DYXML bench.c yxml.o -o yxml
+	${STRIP} -s yxml
+
+yxml-bench: yxml ${FILES}
+	./runbench.sh yxml.o yxml ${FILES}
+
+
+
+strlen: ${MUSLCROSS} bench.c
+	${CC} ${CFLAGS} -DSTRLEN bench.c -o strlen
+	${STRIP} -s strlen
+
+strlen-bench: strlen ${FILES}
+	./runbench.sh strlen strlen ${FILES}
+
+
+
+libexpat.a: ${MUSLCROSS} ${EXPAT}
+	cd ${EXPAT} && ./configure --disable-shared --host=${HOST} && make buildlib
+	cp ${EXPAT}/.libs/libexpat.a libexpat.a
+
+expat: libexpat.a bench.c
+	${CC} ${CFLAGS} -I${EXPAT}/lib -DEXPAT bench.c -L. -lexpat -o expat
+	${STRIP} -s expat
+
+expat-bench: expat ${FILES}
+	./runbench.sh libexpat.a expat ${FILES}
+
+
+
+libxml2.a: ${MUSLCROSS} ${LIBXML2}
+	@# So many options, yet --with-minimum and --without-output don't seem to work
+	cd ${LIBXML2} && ./configure --disable-shared --without-c14n --without-catalog --without-debug\
+		--without-docbook --without-ftp --without-html --without-http --without-iconv --without-iso8859x\
+		--without-legacy --without-pattern --without-push --without-regexps --without-sax1\
+		--without-schemas --without-schematron --without-threads --without-tree --without-valid\
+		--without-writer --without-xinclude --without-xpath --without-xpath --without-modules\
+		--without-zlib --without-lzma --host ${HOST} && make libxml2.la
+	cp ${LIBXML2}/.libs/libxml2.a libxml2.a
+
+libxml2: libxml2.a bench.c
+	${CC} ${CFLAGS} -I${LIBXML2}/include -DLIBXML2 bench.c -L. -lxml2 -o libxml2
+	${STRIP} -s libxml2
+
+libxml2-bench: libxml2 ${FILES}
+	./runbench.sh libxml2.a libxml2 ${FILES}
+
+
+
+libmxml.a: ${MUSLCROSS} ${MXML}
+	cd ${MXML} && ./configure --disable-shared --disable-threads --host ${HOST} && make libmxml.a
+	cp ${MXML}/libmxml.a libmxml.a
+
+mxml: libmxml.a bench.c
+	${CC} ${CFLAGS} -I${MXML} -DMXML bench.c -L. -lmxml -o mxml
+	${STRIP} -s mxml
+
+mxml-bench: mxml ${FILES}
+	./runbench.sh libmxml.a mxml ${FILES}
+
+
+
+clean:
+	rm -f *.a *.o *-bench yxml strlen expat libxml2 mxml
+
+distclean:
+	rm -rf ${MUSLCROSS} ${FILES} ${EXPAT} ${LIBXML2} ${MXML}
diff --git a/bench/bench.c b/bench/bench.c
new file mode 100644
index 0000000..f354dba
--- /dev/null
+++ b/bench/bench.c
@@ -0,0 +1,121 @@
+/* Copyright (c) 2013 Yoran Heling
+
+  Permission is hereby granted, free of charge, to any person obtaining
+  a copy of this software and associated documentation files (the
+  "Software"), to deal in the Software without restriction, including
+  without limitation the rights to use, copy, modify, merge, publish,
+  distribute, sublicense, and/or sell copies of the Software, and to
+  permit persons to whom the Software is furnished to do so, subject to
+  the following conditions:
+
+  The above copyright notice and this permission notice shall be included
+  in all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+  CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+  TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+  SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#if defined(STRLEN)
+static void xmlbench(const char *buf, size_t bufsize) {
+	size_t l = strlen(buf);
+	assert(l == bufsize);
+}
+
+
+#elif defined(YXML)
+#include <yxml.h>
+static void xmlbench(const char *buf, size_t bufsize) {
+	yxml_t x[1];
+	char *stack = malloc(4096);
+	yxml_init(x, stack, 4096);
+	yxml_ret_t r;
+	do {
+		r = yxml_parse(x, *buf);
+		buf++;
+	} while(*buf && r >= 0 && r != YXML_EOD);
+	/*printf("t%03lu l%03u b%03lu: %c %d", x->total, x->line, x->byte, *buf, r);*/
+	assert(!*buf);
+}
+
+
+#elif defined(EXPAT)
+#include <expat.h>
+static void xmlbench(const char *buf, size_t bufsize) {
+	XML_Parser p = XML_ParserCreate(NULL);
+	int r = XML_Parse(p, buf, bufsize, 1);
+	assert(r != XML_STATUS_ERROR);
+	XML_ParserFree(p);
+}
+
+
+#elif defined(LIBXML2)
+#include <libxml/xmlreader.h>
+static void xmlbench(const char *buf, size_t bufsize) {
+	xmlTextReaderPtr p = xmlReaderForMemory(buf, bufsize, NULL, NULL, 0);
+	while(xmlTextReaderRead(p) == 1)
+		;
+	xmlFreeTextReader(p);
+}
+
+
+#elif defined(MXML)
+#include <mxml.h>
+static void sax_cb(mxml_node_t *node, mxml_sax_event_t event, void *data) {
+	static int i;
+	/* Only retain the root node, to make sure that we don't get NULL. */
+	if(event == MXML_SAX_ELEMENT_OPEN && !i) {
+		mxmlRetain(node);
+		i=1;
+	}
+}
+static void xmlbench(const char *buf, size_t bufsize) {
+	mxml_node_t *n = mxmlSAXLoadString(NULL, buf, MXML_NO_CALLBACK, sax_cb, NULL);
+	assert(n);
+}
+
+
+#else
+#error No idea what to bench
+#endif
+
+
+int main(int argc, char **argv) {
+	struct stat st;
+	ssize_t r;
+
+	assert(argc == 2);
+	r = stat(argv[1], &st);
+	assert(!r);
+
+	char *buf = malloc(st.st_size+1);
+	assert(buf);
+
+	int fd = open(argv[1], O_RDONLY);
+	assert(fd > 0);
+
+	ssize_t rd = read(fd, buf, st.st_size);
+	assert(rd == st.st_size);
+	buf[st.st_size] = 0;
+	close(fd);
+
+	xmlbench(buf, st.st_size);
+	return 0;
+}
+
+/* vim: set noet sw=4 ts=4: */
diff --git a/bench/runbench.sh b/bench/runbench.sh
new file mode 100755
index 0000000..77b4e50
--- /dev/null
+++ b/bench/runbench.sh
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+OBJ=$1
+shift
+PROG=$1
+shift
+
+if test -z $OBJ; then
+  echo "This script is supposed to be run from 'make'."
+  exit 1
+fi
+
+echo "====> Benchmark results for $PROG" >$PROG-bench
+size -t $OBJ | tail -n 1 >>$PROG-bench
+wc -c $PROG >>$PROG-bench
+
+for i in $@; do
+  echo "== $i" >>$PROG-bench
+  cat $i >/dev/null
+  sh -c "time ./$PROG $i" >>$PROG-bench 2>&1
+  sh -c "time ./$PROG $i" >>$PROG-bench 2>&1
+done