Direct read/write to strings in modern I6?

So I’m writing some code to output game data to an XML file. At its core, it’s based on three routines:

active-XML-file is an indexed text variable.

To open a tag called (tag - an indexed text):
	now active-XML-file is "[active-XML-file]<[tag]>".
		
To make a tag called (tag - an indexed text) with contents (contents - an indexed text):
	now active-XML-file is "[active-XML-file]<[tag]>[contents]</[tag]>".
		
To close the tag (tag - an indexed text):
	now active-XML-file is "[active-XML-file]</[tag]>".

This works okay, but it starts to slow down for XML files larger than a few hundred characters. My impression is that this is caused by the need to continuously recreate the active-XML-file variable each time something is added to it.

So to speed things up, I want to make a string variable with a set size, and directly append strings to it in I6. (I’m aware this involves a certain amount of bookkeeping re: current cursor position and not overrunning the allocated space.) But my I6 is rusty enough I’m running into troubles remembering how to do this. (I think things are complicated by the fact that I never worked with Glulx in I6.) The DM4’s stuff on how to do this is z-machine based, so it’s not helpful. Also, the various sources I’ve looked at to try to see what to do-- Jesse’s old “String Buffers” extension, the I7 “Appendix B” template layer, the Glulx spec-- all seem to be using totally different routines and styles. I gather there are a few different front-ends in place for some of this low-level stuff, but I’m unsure which if any are used by modern Inform 7.

So my question is: what’s the current, 2010-era way to do string operations in Glulx under I6/I7? How would you write the I6 equivalent of:

To append (txt - some indexed text) to the string buffer:
     now string-buffer is "[string-buffer][txt]".

(except not actually creating a new string, but really appending to the old one? Or is there a better way to get faster speed out of something like this?)

I may have scared everyone off by mentioning XML. Forget I mentioned it. I was not here. I did not say this.

More simply: can someone point me to an example of a 6E72-compatible way of doing direct, character-by-character string manipulation on the I6 level?

The simplest way is to use indexed text, which already has a bunch of low-level functions defined in IndexedText.i6t. Something like this:

[code]To append (Y - indexed text) to (X - indexed text): (- IT_Append({-pointer-to:X}, {-pointer-to:Y}); -).

Include (-
[ IT_Append indt newt i len1 len2;
if ((indt == 0) || (BlkType(indt) ~= INDEXED_TEXT_TY)) return;
if ((newt == 0) || (BlkType(newt) ~= INDEXED_TEXT_TY)) return;
len1 = IT_CharacterLength(indt);
len2 = IT_CharacterLength(newt);
if (BlkValueSetExtent(indt, len1+len2+1) == false) {
print “*** Can’t extend indexed text block ***^”;
return;
}
for (i=0: i<len2: i++)
BlkValueWrite(indt, len1+i, BlkValueRead(newt, i));
BlkValueWrite(indt, len1+len2, 0);
]; -).[/code]

This will still slow down as the strings get long, though: indexed text is null-terminated, so IT_CharacterLength has to count the characters. But it should be faster than appending with “[foo][bar]”, which has to allocate a new string and copy both old strings into it.

A faster but more complicated way would be to build an unencoded (E0) Glulx string, possibly using the allocation functions from Dynamic Tables to obtain the memory.

Pros: if you use the first few bytes for a length counter, it’ll be faster than having to count characters to find the length of the first string; you can still use “say” to print the string, since it will be a plain text value.

Cons: you have to do your own memory management.

A third way in this particular case might be to just print tags to the file as they’re generated, instead of building up a giant string in memory.

Is there any reason you can’t just append the text to the end of the file directly, rather than passing through the text variable? This would bypass the need to keep the entire file in a single indexed text variable. If you need to get the text back out, you can use “text of the file of XML”, so the current state of the file is always accessible as a indexed text if you need it.

If for some reason that doesn’t work, could you use Eric Eve’s Text Capture? That would let you just use say statements, e.g.:

[code]To open a tag called (tag - an indexed text):
say “<[tag]>”.
To close a tag called (tag - an indexed text):
say “<[/tag]>”.

To create the XML file:
start capturing text;
say “[XML file header]”;
open a tag called “XML”;

close a tag called “XML”;
stop capturing text;
write the captured text to file of XML.[/code]

–Erik

Thanks guys-- Jesse’s first method runs faster than what I was trying originally, but writing directly to the file is (surprisingly to me) much faster. I was expecting a turn with hundreds of hits to a file would be slow, but my complex test case finishes in less than a second (whereas the original method took over thirty). Jesse’s IT_Append takes about five seconds.

I haven’t yet tried implementing Jesse’s unencoded string method, which I expect would be faster still-- but the direct file i/o seems to work for the sizes I’m dealing with.