I have been getting questions concerning the performance of Tango in the XML benchmarks I have been running, with people wondering how something that is not C/C++ could be so fast. “They must be cheating!”
This post intends to explain how D, and subsequently Tango, can perform so well, even against C/C++. To read more about D, please visit the home page for D - D Programming Language. Tango is an alternate ’standard’ library for the D programming language, with a design philosophy of building a great library, with extensive documentation, and providing the greatest functionality in the most efficient manner possible. How do they do that you ask?
- Array slicing - Using D’s array slicing feature, you don’t need to create a copy of a string in parsing. Tango maps the DOM nodes directly onto the input buffer, so for any part of the XML, only one text version exists, that being the input. VTD-XML does something similar, and RapidXml does similar, but actually mutates the input buffer while parsing, so creates one copy before starting the parse. Java6 SAX, on the other hand, turns character arrays into strings for element and attribute names, etc, and that means more allocation.
- Native code - Yes, yes, I know. C/C++ are native as well. I write this because Java is not, and while todays’ JIT can compete and in cases outrun native code, if you are allocating hundreds of megabytes during processing, you are going to slow down in garbage collection. D is native code, to be fast right out of the gate, but also uses garbage collection. This just shows that the approach to design is MORE important than the choice of language. You will note that RapidXml is competing on speed, but when it comes to competing on efficency (speed/ram used), it loses, because it copies the input buffer to modify it during parse. Both RapidXml and Tango use internally managed freelists for nodes in the tree. One tweak to the RapidXml design could improve its efficiency an order of magnitude.
- Avoid heap allocation as much as possible - More allocation means more CPU time. Tango’s internal philosophy is one in which you allocate as little as possible, and allocate on the stack if possible. This makes the code much faster.
- Design choices - Tango XML has made specific design decisions which affect the speed, but can impact the ability of the parser compared to the parser that you are used to using today. For example, Tango XML, in its fastest incarnation, does NOT do entity decoding. If you are down at a system level, and you are building some xml proxy, you don’t need to do this. These sorts of design decisions are starting to show in the fastest/newest xml parsers. RapidXml fastest configuration does the same. Java StaX parsers have an option for the same. These sorts of omissions by design can be layered on top of the existing parser, so that YOU can make the tradeoff between speed and a particular feature. It seems as though good library design is a lost art since some point in the late 70’s/early 80’s. Features over efficiency have prevailed in the last 2 decades because ‘CPU is less expensive than programmer time’. I am not saying this is bad, just showing that good design, both in language and standard library, can create some impressive numbers.
Comments are open if other D people would like to add their $.02.
Popularity: 16%
I created a benchmark similar to the one that VTD-XML uses. Basically, since most xml processing is mutation, this benchmark parses an input xml file, executes various xpaths on the file, modifying the document in 2 instances, and then serializes the new document. The steps are listed below:
- Parse blog.xml, preparing to query the resulting document
- Perform the following xpath queries, or their equivalents, once each:
- count(//*) (10390 for this document)
- //item (a list of those 10390 items)
- /blog/item (similar to the previous, except you know the path)
- //text() (all text nodes)
- count(//item)
- count(/blog/item)
- /blog/item[@num=’a781′]
- /blog/item/body/p/a
- Mutate the document by removing the resulting nodes from the last 2 queries (performed inline with the queries)
- serialize the modified document back out
I created this benchmark for 4 products (the ones that have xpath or xpath-like support, if you know of another one, please submit me some code, and I will be happy to run and aggregate the results):
After the run, I take the average cycle time, and turn that into the followin graph showing cycles per second. blog.xml is 1.3MB, so you can multiply these numbers by 1.3 to get the Megabytes per second number for each tool.

Some notes of the implementations:
- Tango, while not actually having an actual xpath parser, has the requisite power in its query language to be able to pull this off with aplomb
- You will note that the VTD code does NOT delete the /blog/item[@num=’a781′] node, because its XMLModifier is unable to perform deletes inside a delete. If someone knows how to fix this, please let me know
Would also note that these benchmarks were run on an Intel Q6700 quad core machine at 2.66 GHz, with 4GB of RAM, running Ubunu Linux.
Popularity: 7%
I have started writing this post as a sidebar in comparing the parsers in my benchmarks. I will post what I know, and add more to it as I am informed by the community. Consider this a living post. Where something is just a fact, I list it as a Pro, such as language developed.
| Product |
Pros |
Cons |
| Tango PullParser (pull) |
- Written in the D programming language
- Tango devs are very aware of cost of allocation, and try to avoid it as often as possible.
- Extremely fast, extremely memory efficient
|
- Beta level code
- Interfaces may change, since Tango is not yet 1.0
- NOT W3C XML compliant (ignores DOCTYPE, etc)
|
| Tango SaxParser (SAX) |
- Written in the D programming language, on top of Tango’s PullParser.
- Straight port of Java SAX code, with a small amount of D flavor
- Useful for porting existing SAX-based code
|
- Beta level code
- Interfaces may change, since Tango is not yet 1.0
- As shown in the benchmarks, virtual calls (SAX does a lot of them) cost quite dearly
- NOT W3C XML compliant
|
| Tango Document (DOM) |
- Written in the D programming language, and a DOM-style tree of xml to manipulate
- Faster than all non-tree code tested so far
- Not DOM compliant
- Integrated query language, inspired by XPath
|
- Beta level code
- Interfaces may change, since Tango is not yet 1.0
- Not DOM compliant
- NOT W3C XMLcompliant
|
| Phobos std.xml (DOM) |
- Written in the D programming language
- Shipped in D 2.0’s standard library
- DOM-style tree object model
- Not DOM compliant
|
- Not DOM compliant
- Requires previous knowledge of the structure of the xml being parsed. Cannot parse arbitrary XML
- NOT W3C compliant
|
| RapidXml (DOM) |
- Written in C++, with ultimate performance in mind
- Highly configurable, use only the featureset you need.
- Not DOM compliant
|
- Not DOM compliant
- Not W3C XML compliant (ignores DOCTYPE)
|
| libxml2 (SAX) |
- Written in C
- extremely robust - passes all 1800 tests from the OASIS XML Tests Suite
|
|
| VTD-XML (DOM) |
- Written in Java, also availabe in C, C#
- Indexes the XML for super fast querying
- XPath Support
|
|
| Java SAX (SAX) |
|
|
| Java DOM (DOM) |
- Written in Java
- W3C DOM compliant
- W3C XML compliant
- XPath support
|
|
| Java StaX parsers (pull)(includes Aalto, Woodstox, and javolution) |
|
|
| DOM4J (DOM) |
- Written in Java
- XPath Support
|
|
Popularity: 7%
I was helping someone on IRC in #d.tango try to use tango.text.xml to parse and display data from an xml document. We ended up building a simple example using HttpGet to get the document, Document to parse it, and Document’s xpath-like querying functionality to extract the useful bits.
import tango.io.File;
import tango.io.Stdout;
import tango.text.xml.Document;
import tango.net.http.HttpGet;
void main ()
{
auto doc = new Document!(char);
auto page = new HttpGet (\"http://www.google.com/ig/api?weather=London\");
auto content = cast (char[]) page.read;
doc.parse (content);
foreach( node; doc.query.descendant[\"forecast_conditions\"])
{
Stdout.formatln(\"forecast for {} is {} with a high of {}\",
node.query[\"day_of_week\"].attribute.nodes[0].value,
node.query[\"condition\"].nodes[0].getAttribute(\"data\").value,
node.query[\"high\"].nodes[0].getAttribute(\"data\").value);
}
}
The D programming language coupled with Tango as a standard library allows you to become a productive programmer.
Update: Please ignore the backslashes in the code if you are trying to run this example. For some reason, Wordpress is mucking around with the output.
Popularity: 7%
From my mistaken typing in the aalto benchmark, I accidentally benchmarked the default Java6 StaX parser, so this graph changes the axis to allow more players, and adds the real Aalto numbers. Click to view the graphs in full size.

Popularity: 8%
Thanks to Paul Findlay, we finally have a possible contender in the Java camp with Aalto.


This goes to show you how good library design and the D Programming Language come together to kick serious butt.
PS: I am looking for anyone to do comparisons with MSXML, RapidXML, etc. More native code help is needed. Send me email at scott aht dotnot daht org.
Popularity: 8%
I added Java DOM to the graphs. Building a tree in memory is not the fastest way to parse a doc, but it is the easiest way to modify the doc after parsing. Java 6 DOM shows off not too terribly bad in the parsing speed, but with all the allocation going on, RAM usage skyrockets, and the efficiency graph shows the pain.


This goes to show you how good library design and the D Programming Language come together to kick serious butt.
Popularity: 8%
Speed master Kris made some changes to Tango’s xml libraries today, and increased the performance of the parser to over 500MB/second! The machine is still the quad core 2.66GHz Intel box running Linux with 4GB of RAM. This run reflects revision 3286 of Tango SVN.
I will only update the images here, I think you should now know how I obtained them…


While SAX is showing slower in speed than DOM in Tango (I hope that is as weird to read as it was for me to write), you can see that the RAM usage graph puts it back into perspective.
I also forgot to note that this quad core box is now capable of parsing XML at over 2GB/sec if all 4 cores are used. Impressive indeed.
Tango is an alternate standard library for the D Programming Language.
Popularity: 8%
Here is the current summary of the benchmarks run so far in a graphical form:

I hope to add more (libxml2, Xerces-C, etc) in the future. If you have C++ chops, I am looking for someone to code up one for MSXML. I will also be adding some Java benchmarks in here as well.
Update 2008-02-23 20:57 PST - Since Nietsnie was kind enough to donate his machine time, I re-ran all the current benchmarks on his box, to be able to include the libxml2 sax numbers as apples to apples. The graph is now updated, and includes the speed (Megabytes per second). Thanks to Robert Fraser for catching that.
The current benchmarking machine is an Ubuntu box with 4GB RAM sporting a quad-core Intel chip at 2.66GHz. In other words, much faster than my machine.
Popularity: 9%
I hesitate to publish these numbers, as they are not direct apples to apples comparison. The reason is that the D Programming Language version 2.0’s std.xml is an xml parser, but one where you must know the schema beforehand, and register handlers for each element by name. I was unwilling/too lazy to write said handlers for the docs I was doing, so I found a method called check(), that according to the source code comments makes sure that a document is well-formed, and contains no bad characters. That’s as close as I am going to get to parsing these docs without code help from the community, so take this with a grain of salt or two. I am using DMD 2.011, using stdxml.d to benchmark, listed here:
module stdxml;
import std.stdio;
import std.xml;
import std.perf;
void benchmark (int iterations, invariant char[] content) {
auto elapsed = new HighPerformanceCounter();
elapsed.start;
for (auto i=0; ++i < iterations;) {
check(content);
}
elapsed.stop;
float timer = elapsed.milliseconds / 1000.0;
auto total = (content.length * iterations) / (timer * (1024 * 1024));
writef(total);
writefln(" MB/s");
}
void main()
{
invariant char[] content = import ("hamlet.xml");
for (int i = 11; --i;)
benchmark (10, content);
}
You will note that iterations are way down compared to others, because I really couldn’t wait around all night for the results. Results are:
D:\d2>dmd\bin\dmd -J. stdxml.d
D:\d2\dmd\bin\..\..\dm\bin\link.exe stdxml,,,user32+kernel32/noi;
D:\d2>stdxml
1.65656 MB/s
1.68694 MB/s
1.67213 MB/s
1.68908 MB/s
1.67318 MB/s
1.68481 MB/s
1.68268 MB/s
1.68162 MB/s
1.68588 MB/s
1.67951 MB/s
Average checking speed: 1.68MB/sec. Can I get some help from phobos people? Scripting languages are faster than this… Results for soap_mid.xml:
D:\d2>dmd\bin\dmd -J. stdxml.d
D:\d2\dmd\bin\..\..\dm\bin\link.exe stdxml,,,user32+kernel32/noi;
D:\d2>stdxml
1.18841 MB/s
1.22127 MB/s
1.2201 MB/s
1.2201 MB/s
1.23065 MB/s
1.21894 MB/s
1.22829 MB/s
1.22829 MB/s
1.22829 MB/s
1.23065 MB/s
Average checking speed: 1.22 MB/sec. So it is not just Tango that slows down with the attributes…
If someone from the phobos community wants to update the code run here, just leave a comment or send me mail privately. scott at you can guess where.
Update 2008-02-23 19:57 PST
Running on a quad core 2.66GHz box yielded:
stonecobra@jeff-home:~/xmlbench$ ~/d2/dmd/bin/dmd -J. stdxml.d
gcc stdxml.o -o stdxml -m32 -Xlinker -L/home/stonecobra/d2/dmd/bin/../lib -lphobos2 -lpthread -lm
stonecobra@jeff-home:~/xmlbench$ ./stdxml
6.47343 MB/s
6.50501 MB/s
6.5691 MB/s
6.48918 MB/s
6.50501 MB/s
6.50501 MB/s
6.52092 MB/s
6.48918 MB/s
6.52092 MB/s
6.47343 MB/s
stonecobra@jeff-home:~/xmlbench$ vi stdxml.d
stonecobra@jeff-home:~/xmlbench$ ~/d2/dmd/bin/dmd -J. stdxml.d
gcc stdxml.o -o stdxml -m32 -Xlinker -L/home/stonecobra/d2/dmd/bin/../lib -lphobos2 -lpthread -lm
stonecobra@jeff-home:~/xmlbench$ ./stdxml
4.39338 MB/s
4.3979 MB/s
4.38586 MB/s
4.37986 MB/s
4.40244 MB/s
4.37986 MB/s
4.39338 MB/s
4.40092 MB/s
4.37089 MB/s
4.37089 MB/s
Average for hamlet.xml: 6.51 MB/sec.
Average for soap_mid.xml: 4.39 MB/sec.
PS: I also wanted to note for any naysayers, that I left off -O -release and -inline because the phobos example actually runs SLOWER with any and/or all of these flags. I am not trying to slip anything by anyone here.
Popularity: 5%
Next is Tango’s SaxParser, a SAX API layered on top of PullParser for the D Programming Language. It passes parsing events through to a handler, push-style. I used the current SVN HEAD of Tango, which is current revision 3247, and compiled with DMD v1.024. I count the number of elements, attributes, and text nodes, along with their lengths, to attempt to compare to the benchmarks here. Apparently, Tango is beating them masterfully. soap_mid.xml is the same file (by size, and I suspect, origin) as their “soap2.xml”. And they have an extra 200MHz of CPU in their benchmark. The benchmark code used was xmlsax.d, listed here:
module xmlsax;
import tango.io.Stdout;
import tango.time.StopWatch;
import tango.text.xml.SaxParser;
void benchmark (int iterations, SaxParser!(char) parser, char[] content)
{
StopWatch elapsed;
elapsed.start;
for (auto i=0; ++i < iterations;)
{
parser.parse;
parser.reset;
}
Stdout.formatln ("{} MB/s", (content.length * iterations) / (elapsed.stop * (1024 * 1024)));
}
void main()
{
auto content = import ("hamlet.xml");
auto parser = new SaxParser!(char);
auto handler = new LengthHandler!(char);
parser.setSaxHandler(handler);
parser.setContent(content);
for (int i = 11; --i;)
benchmark (2000, parser, content);
}
private class LengthHandler(Ch = char) : SaxHandler!(Ch) {
public uint elm;
public uint att;
public uint txt;
public uint elmlen;
public uint attlen;
public uint txtlen;
public void startElement(Ch[] uri, Ch[] localName, Ch[] qName, Attribute!(Ch)[] atts) {
elm++;
elmlen += localName.length;
foreach (inout attr; atts) {
att++;
attlen += attr.localName.length;
}
}
public void characters(Ch[] ch) {
txt++;
txtlen += ch.length;
}
}
Results for hamlet.xml:
D:\d\tango\example\text>jake xmlsax.d -O -release -inline -J.
d:\d\dmd\bin\..\..\dm\bin\link.exe xmlsax+Stdout+Print+IBuffer
D:\d\tango\example\text>xmlsax
258.59 MB/s
259.45 MB/s
258.91 MB/s
258.70 MB/s
258.79 MB/s
259.27 MB/s
259.37 MB/s
259.94 MB/s
258.72 MB/s
258.64 MB/s
Average parsing speed: 259.04 MB/sec. Results for soap_mid.xml:
D:\d\tango\example\text>jake xmlsax.d -O -release -inline -J.
d:\d\dmd\bin\..\..\dm\bin\link.exe xmlsax+Stdout+Print+IBuffer
D:\d\tango\example\text>xmlsax
179.96 MB/s
180.15 MB/s
180.97 MB/s
179.67 MB/s
180.76 MB/s
180.46 MB/s
179.90 MB/s
178.61 MB/s
179.20 MB/s
180.47 MB/s
Average parsing speed: 180.02 MB/sec. Sax seems to do a bit better than DOM with the attributes, but still shows a significant overhead to PullParser.
Average for hamlet.xml: 347.63 MB/sec.
Average for soap_mid.xml: 240.41MB/sec.
Next is Tango’s Document, a DOM-ish parser built on top of PullParser fro the D Programming Language. It builds an in-memory tree of the document being parsed, which can then be easily navigated/edited in-memory. I used the current SVN HEAD of Tango, which is current revision 3247, and compiled with DMD v1.024. The benchmark code used was xmldom.d, listed here:
import tango.io.Stdout;
import tango.time.StopWatch;
import tango.text.xml.Document;
/*******************************************************************************
*******************************************************************************/
void bench (int iterations)
{
StopWatch elapsed;
auto doc = new Document!(char);
auto content = import (”hamlet.xml”);
elapsed.start;
for (auto i=0; ++i < iterations;)
doc.parse (content);
Stdout.formatln ("{} MB/s", (content.length * iterations) / (elapsed.stop * (1024 * 1024)));
}
/*******************************************************************************
*******************************************************************************/
void main()
{
for (int i=11; --i;)
bench (2000);
}
It was compiled using: jake xmldom.d -O -release -inline -J.
Resulting run was:
D:\d\tango\example\text>jake xmldom.d -O -release -inline -J.
d:\d\dmd\bin\..\..\dm\bin\link.exe xmldom+Stdout+Print+IBuffer…
D:\d\tango\example\text>xmldom
240.39 MB/s
239.77 MB/s
239.34 MB/s
240.70 MB/s
242.35 MB/s
241.36 MB/s
241.66 MB/s
242.81 MB/s
241.96 MB/s
241.92 MB/s
Average of the resulting run: 241.23 MB/sec parsing. That is one speedy little DOM builder. Run for soap_mid.xml brought back:
D:\d\tango\example\text>jake xmldom.d -O -release -inline -J.
d:\d\dmd\bin\..\..\dm\bin\link.exe xmldom+Stdout+Print+IBuffer
D:\d\tango\example\text>xmldom
117.36 MB/s
118.34 MB/s
118.28 MB/s
118.38 MB/s
117.57 MB/s
118.28 MB/s
118.73 MB/s
118.22 MB/s
118.12 MB/s
118.63 MB/s
Average of the runs was 118.19 MB/sec parsing. Looks like a similar result to PullParser. Attributes must have a fairly high cost in this implementation.
Average for hamlet.xml: 336.07 MB/sec.
Average for soap_mid.xml: 166.47MB/sec.
First up, Tango’s tango.text.xml.PullParser. You instantiate the parser, start the parse, and then continue to ask for the next ‘node’. I used the current SVN HEAD of Tango, which at the time of writing was revision 3247, compiled with DMD v1.024. The benchmark code ran is xmlpull.d, and is listed here:
import tango.io.Stdout;
import tango.time.StopWatch;
import tango.text.xml.PullParser;
void benchmark (int iterations)
{
StopWatch elapsed;
auto content = import (”hamlet.xml”);
auto parser = new PullParser!(char) (content);
elapsed.start;
for (auto i=0; ++i < iterations;)
{
while (parser.next) {}
parser.reset;
}
Stdout.formatln ("{} MB/s", (content.length * iterations) / (elapsed.stop * (1024 * 1024)));
}
void main()
{
for (int i = 11; --i;)
benchmark (2000);
}
It was compiled with the command: jake xmlpull.d -O -release -inline -J.. Results of the run:
D:\d\tango\example\text>jake xmlpull.d -O -release -inline -J.
d:\d\dmd\bin\..\..\dm\bin\link.exe xmlpull+Stdout+Print+IBuffer+
D:\d\tango\example\text>xmlpull
316.82 MB/s
315.71 MB/s
315.49 MB/s
317.55 MB/s
316.77 MB/s
316.69 MB/s
316.64 MB/s
317.43 MB/s
316.16 MB/s
317.90 MB/s
Average of the resulting run: 316.72 MB/sec parsing. Replacing hamlet.xml with soap_mid.xml in the above code results in:
D:\d\tango\example\text>jake xmlpull.d -O -release -inline -J.
d:\d\dmd\bin\..\..\dm\bin\link.exe xmlpull+Stdout+Print+IBuffer+ICon
D:\d\tango\example\text>xmlpull
229.19 MB/s
227.78 MB/s
229.12 MB/s
229.71 MB/s
228.63 MB/s
229.40 MB/s
228.98 MB/s
229.21 MB/s
230.02 MB/s
228.56 MB/s
Average of the resulting run: 229.06. Lower than hamlet.xml, probably due to the attribute processing required, but also possibly the lack of whitespace.
Tango has landed XML support in the tango.text.xml package. Current highlights include a pull parser, a DOM parser, and a SAX parser, as well as a budding XPath like package.
What makes these different you ask? Why another damn XML parser? Glad you asked. These components are intended to be high-speed, non-allocating tools that can be used at a server or appliance level with much less overhead than other solutions. For example, the SAX parser needs just a few KB of memory over and above the size of the content being parsed.
If you need a fast XML parser, check them out. I am still writing up my benchmarking output, so stay tuned for a post on that shortly.
What is Tango? Tango is an alternate standard library for the D Programming Language.
Popularity: 4%
Just finished the GC API for the OO wrapper of dlua. I think the OO paradigm is really starting to show well.
An example:
import lua.wrap.lua;
import tango.io.Console;
import tango.io.Stdout;
void main()
{
Lua lua = new Lua();
Stdout.formatln (”init:\t{} bytes of memory”, lua.gc.memoryBytesUsed);
lua.execute(”print(’Hello World!’)”);
Stdout.formatln (”pre-gc:\t{} bytes of memory”, lua.gc.memoryBytesUsed);
lua.gc.stop();
lua.gc.restart();
Stdout.formatln (”start:\t{} bytes of memory”, lua.gc.memoryBytesUsed);
lua.gc.full();
Stdout.formatln (”post:\t{} bytes of memory”, lua.gc.memoryBytesUsed);
}
Output from that program on my Windows machine:
D:\d\dlua\examples\wrap>GCExample
init: 19254 bytes of memory
Hello World!
pre-gc: 19577 bytes of memory
start: 19577 bytes of memory
post: 19093 bytes of memory
As you can see, Lua is fairly memory efficient, and also code efficient, weighing in at about 30K of overhead compared to a basic “Hello World” D application, 132K vs 162K. To be fair, including Lua also means you are distributing a 132K DLL for the real Lua functionality.
Lua is a good fit for the D Programming Language, as shown here.
Popularity: 22%
… I was just uninformed. Anders has created a fantastic set of the C bindings in D over at Google code: dlua.
The module lua.c is the original C bindings ported to the D Programming Language, changing as little as possible. The module lua.wrap contains an Object-Oriented D wrapper to the C bindings, and allows simple interaction without knowing the C Lua API. Both lua.c and lua.wrap are tango and phobos compatible, and should work on any platform for which D and Lua are available. A simple example:
private import lua.wrap.lua;
version (Tango)
import tango.io.Console;
else
import std.stdio : Cout = writef;
extern (C) int testfunction(State L)
{
Cout(”Hello from D!\n”);
return 0;
}
void main()
{
Lua lua = new Lua();
lua.register(&testfunction, “testfunction”);
lua.execute(”testfunction()”);
}
The main() function is the bit to look at. The intention is to have a simple, powerful wrapper for the Lua engine so that D developers can expose scripting functionality in their own code.
I am currently testing out how to add runtime reflection into the wrapper, so you can just register an object and have it operable in the Lua engine, without having to register each member and function.
Popularity: 23%
I have been looking for a scripting engine to embed in my D code, and since DMDScript was GPL, I needed something different.
Enter Lua.
After trying to find the right binary library, I finally got the Simple Lua API Example working in the D Programming Language.
So far, here is the header conversion that you need to make the example work:
module lua;
extern(C):
version (Windows) {
pragma (lib, “lua.lib”);
//extern (Windows):
} else {
pragma (msg, “You will need to manually link in the Lua library.”);
}
int LUA_MULTRET = -1;
int LUA_GLOBALSINDEX = -10002;
alias void lua_State;
extern (C) void lua_close (lua_State *L);
extern (C) lua_State *luaL_newstate();//lua_open()
extern (C) void luaL_openlibs(lua_State *L);
extern (C) int luaL_loadfile(lua_State *, char *);
extern (C) void lua_createtable(lua_State *, int=0, int=0);
extern (C) void lua_pushnumber(lua_State *, double=0.0);
extern (C) void lua_rawset(lua_State *, int);
extern (C) void lua_setfield(lua_State *, int, char *);
extern (C) int lua_pcall(lua_State *, int, int, int);
extern (C) double lua_tonumber(lua_State *, int);
extern (C) int lua_gettop(lua_State *);
extern (C) void lua_settop(lua_State *, int);
void lua_setglobal(lua_State *L, char* s) {
lua_setfield(L, LUA_GLOBALSINDEX, s);
}
void lua_pop(lua_State *L, int n=0) {
lua_settop(L, -(n)-1);
}
And here is the C example converted to D:
private import tango.util.log.Configurator, tango.util.log.Log;
private import tango.text.convert.Sprint;
private import lua;
void main() {
Configurator();
testSimple;
}
void testSimple() {
auto log = Log.getLogger(”testlua.testsimple”);
auto sprint = new Sprint!(char);
lua_State *L;
//AKA - L = lua_open();
L = luaL_newstate();
luaL_openlibs(L);
int status;
status = luaL_loadfile(L, “script.lua”);
log.info(sprint(”loadfile status: {}”, status));
/*
* Ok, now here we go: We pass data to the lua script on the stack.
* That is, we first have to prepare Lua’s virtual stack the way we
* want the script to receive it, then ask Lua to run it.
*/
log.info(”new table”);
lua_createtable(L);
/*
* To put values into the table, we first push the index, then the
* value, and then call lua_rawset() with the index of the table in the
* stack. Let’s see why it’s -3: In Lua, the value -1 always refers to
* the top of the stack. When you create the table with lua_newtable(),
* the table gets pushed into the top of the stack. When you push the
* index and then the cell value, the stack looks like:
*
* < - [stack bottom] -- table, index, value [top]
*
* So the -1 will refer to the cell value, thus -3 is used to refer to
* the table itself. Note that lua_rawset() pops the two last elements
* of the stack, so that after it has been called, the table is at the
* top of the stack.
*/
log.info("load the table");
for (int i = 1; i <= 5; i++) {
lua_pushnumber(L, i); /* Push the table index */
lua_pushnumber(L, i*2); /* Push the cell value */
lua_rawset(L, -3); /* Stores the pair in the table */
}
/* By which name is the script going to reference our table ? */
lua_setglobal(L, "foo");
/* Ask Lua to run our little script */
int result = lua_pcall(L, 0, LUA_MULTRET, 0);
log.info(sprint("pcall() status: {}", result));
/* Get the returned value at the top of the stack */
double sum = lua_tonumber(L, lua_gettop(L));
log.info(sprint("sum is: {}", sum));
lua_pop(L, 1); /* Take the returned value out of the stack */
log.info("Lua SimpleTest complete.");
/* Remember to destroy the Lua State */
lua_close(L);
}
Put your lua.lib and lua.dll in the appropriate places, and build:
D:\d\sc\lua>dsss clean
D:\d\sc\lua>dsss build testlua.d
testlua.d => testlua
+ D:\d\dsss-0.52-dmd-win\bin\rebuild.exe -Idsss_imports\ -I. -S.\ -ID:\d\dsss-0.52-dmd-win\include\d -SD:\d\dsss-0.52-dmd-win\lib\ -oqdsss_objs testlua.d -oftestlua
d:\d\dm\bin\link.exe dsss_objs\testlua+dsss_objs\tango-util-log-Configurator+dsss_objs\tango-util-log-Log+dsss_objs\tango-util-log-Logger+dsss_objs\tango-util-log-Appender+dsss_objs\tango-util-log-Event+dsss_objs\tango-sys-Common+dsss_objs\tango-util-time-Clock+dsss_objs\tango-util-time-Date+dsss_objs\tango-core-Type+dsss_objs\tango-util-log-model-ILevel+dsss_objs\tango-util-log-model-IHierarchy+dsss_objs\tango-util-log-Layout+dsss_objs\tango-uti
l-log-Hierarchy+dsss_objs\tango-util-log-ConsoleAppender+dsss_objs\tango-io-Console+dsss_objs\tango-io-Buffer+dsss_objs\tango-io-model-IBuffer+dsss_objs\tango-io-model-IConduit+dsss_objs\tango-io-DeviceConduit+dsss_objs\tango-io-Conduit+dsss_objs\tango-text-convert-Sprint+dsss_objs\tango-text-convert-Layout+dsss_objs\tango-text-convert-Utf+dsss_objs\tango-text-convert-Float+dsss_objs\tango-text-convert-Integer+dsss_objs\lua,testlua,,user32+kernel
32/noi+.\\+D:\d\dsss-0.52-dmd-win\lib\\;
And now run the example:
D:\d\sc\lua>testlua
1 INFO testlua.testsimple - loadfile status: 0
1 INFO testlua.testsimple - new table
1 INFO testlua.testsimple - load the table
The table the script received has:
1 2
2 4
3 6
4 8
5 10
Returning data back to C
3 INFO testlua.testsimple - pcall() status: 0
3 INFO testlua.testsimple - sum is: 30.00
4 INFO testlua.testsimple - Lua SimpleTest complete.