Posted: 9th Jun 2017 11:55
Here is a piece of code that processes the concatenation of strings at high speed. I needed a way to export my DBO files to X format (including animated skinned meshes) in a fast way, so this code is utilized in that particular app. The code is undergoing optimisation, but the original code is here for now. Feel free to do with it what you want, improve and experiment with it:
[updated code 01Jul2018-now removed function CalcTotalStringBytes. ParseString function now returns string length and totsz is incremented with each call to ParseString, see updated code below]
Try changing the "iter" variable to a lot higher and compare time it takes to perform the concatenation using the traditional method to the faster alternative method.

Method 1

DWORD string ptr = FillMemWithString(s$,slen) -> calls FillMemWithString function
string length = ParseString(s$)
concat string bank ptr = ConcatStrings(bankid,totalbytes,iter)
DeallocateMemory(bankid)

+ Code Snippet
fastfile$="debug_fast.txt"
iter=1000000

dim strptr() 
DeleteFile(fastfile$)
open to write 1,fastfile$
 
timer()=0
startt=timer()
tlen=0 
s$="This is my concatenated string " //+crnl 
inc tlen,ParseString(s$)
s$="and its really cool " //+crnl
inc tlen,ParseString(s$)
s$="and very fast " //+crnl
inc tlen,ParseString(s$)
s$="and very flexible " //+crnl
inc tlen,ParseString(s$)
s$="and I love it."+crnl
inc tlen,ParseString(s$) 

`print tlen

bid=reserve free bank()
bptr=ConcatStrings(bid,tlen,iter)

endt=timer()-startt
endt=endt/1000
 
write string 1,"duration (fast): "+str$(endt)
write string 1,peek string(bptr)
close file 1

sync
wait key

DeallocateMemory(bid)

message "finished ok."
end

//==============================================
// GENERAL STANDARD FUNCTIONS 
function DeleteFile(f$)
    if file exist(f$) then delete file f$
endfunction

//==============================================
// METHOD 1 FUNCTIONS
function FillMemWithString(s$,slen)
	dwstr as dword
	dwstr = make memory (slen+1)
	fill memory dwstr,0,slen+1
	poke string dwstr,s$
endfunction dwstr

function ParseString(s$)
	slen = len(s$)
	maddr=FillMemWithString(s$,slen)
	array insert at bottom strptr() 
	strptr()=maddr
endfunction slen

function ConcatStrings(bankid,totalbytes,iter)
	totalbytes = totalbytes*iter
	make bank bankid,totalbytes+1
	concat = get bank ptr(1)
	fill memory concat,0,totalbytes
	lastsz = 0
	for i=1 to iter
		for p=0 to array count(strptr())
			if i=1 and p=0
				poke string concat,peek string(strptr(p))
			else
				poke string concat+lastsz,peek string(strptr(p))
			endif
			inc lastsz, len(peek string(strptr(p)))
		next p
	next i
endfunction concat

function DeallocateMemory(bankid)
	release reserved bank bankid
	delete bank bankid
	for p = 0 to array count(strptr())
		delete memory strptr(p)
	next p
	undim strptr()
endfunction



Method 2

DWORD memory ptr = FastConcatStrings(tbytes,iter)

+ Code Snippet
iter=1000000
numstrings=5
dim tmpptr(numstrings-1)   
DeleteFile(fastfile$)
open to write 1,fastfile$
 
timer()=0
startt=timer()

s1$="This is my concatenated string " //+crnl 
s2$="and its really cool " //+crnl
s3$="and very fast " //+crnl
s4$="and very flexible " //+crnl
s5$="and I love it."+crnl

tmpptr(0)=get string ptr(s1$)
tmpptr(1)=get string ptr(s2$)
tmpptr(2)=get string ptr(s3$)
tmpptr(3)=get string ptr(s4$)
tmpptr(4)=get string ptr(s5$)

for i = 0 to numstrings-1
    // assigning the peek string to a var is slightly slower, however
    // assigning the len to a var is slightly faster
    tl=len(peek string(tmpptr(i)))  
    inc slen,tl
next i
 
saddr = FastConcatStrings(slen,iter)
 
endt=timer()-startt
endt=endt/1000
 
write string 1,"duration (fast): "+str$(endt)
write string 1,peek string(saddr)
close file 1
sync
wait key

message "finished ok."




function FastConcatStrings(tbytes,iter)
    tbytes = tbytes*iter
    cmem = make memory(tbytes+1) 
    fill memory cmem,0,tbytes
    lastsz = 0
    tmparycnt=array count(tmpptr())
    for i=1 to iter
        for p=0 to tmparycnt
            if i=1 and p=0
                poke string cmem,peek string(tmpptr(p))
            else
                poke string cmem+lastsz,peek string(tmpptr(p))
            endif
            inc lastsz, len(peek string(tmpptr(p)))
        next p
    next i
endfunction cmem



Method 3
+ Code Snippet
#constant crnl chr$(13)+chr$(10)    
fastfile$="debug_fast.txt"
iter=100000
nstr=5

dim tptr(nstr-1)   
dim s$(nstr-1)
dim t$(nstr-1)

DeleteFile(fastfile$)
open to write 1,fastfile$
 
timer()=0
startt=timer()
 
// get array pointer for array holding list of strings 
sptr = get arrayptr(s$())
s$="This is my concatenated string " //+crnl 
AddConcatString(s$,0,sptr)
s$="and its really cool " //+crnl
AddConcatString(s$,1,sptr)
s$="and very fast " //+crnl
AddConcatString(s$,2,sptr)
s$="and very flexible " //+crnl
AddConcatString(s$,3,sptr)
s$="and I love it."+crnl
AddConcatString(s$,4,sptr)
 
s$=free string()
 
for i=0 to nstr-1 
    tptr(i)=get string ptr(s$(i))
    tl=len(peek string(tptr(i)))  
    inc slen,tl
next i
 
saddr = FastConcatStrings(slen,iter)
 
endt=timer()-startt
endt=endt/1000
 
write string 1,"duration (fast): "+str$(endt)
write string 1,peek string(saddr)
close file 1

DeallocateMemory()

end
//==============================================
function AddConcatString(src$,i,iptr)
    link array t$(), iptr
    t$(i)=src$
    unlink array t$()
endfunction
 
function FastConcatStrings(tb,iter)
    tb = tb*iter
    cmem = alloc(tb+1) 
    fill memory cmem,0,tb
    lsz = 0
    tcnt=array count(tptr())
    for i=1 to iter
        for p=0 to tcnt
            if i=1 and p=0
                poke string cmem,peek string(tptr(p))
            else
                poke string cmem+lsz,peek string(tptr(p))
            endif
            inc lsz, len(peek string(tptr(p)))
        next p
    next i
endfunction cmem
 
function DeallocateMemory()
    tcnt=array count(tptr())
    for p = 0 to tcnt
        delete memory tptr(p)
    next p
    undim tptr()
endfunction
 
function DeleteFile(f$)
    if file exist(f$) then delete file f$
endfunction




Method 4:
StrCat function to be updated using link array.
+ Code Snippet
Rem ***** Main Source File *****
sync on : sync rate 0 : sync
#constant crnl chr$(13)+chr$(10)    

dim strlist(4) as string
dim tptr(4) as dword
saryptr=get arrayptr(strlist())
taryptr=get arrayptr(tptr())
iter=1			` 0 milliseconds to process
`iter=1000000	` 2 seconds to process
`iter=2500000	` 5 seconds to process
`iter=5000000	` 10 seconds to process

timer()=0
starttm=timer()
s1$="This is string 1. "
StrAdd(s1$,saryptr,0)

s2$="This is string 2. "
StrAdd(s2$,saryptr,1)

s3$="This is string 3. "
StrAdd(s3$,saryptr,2)

s4$="This is string 4. "
StrAdd(s4$,saryptr,3)

s5$="This is string 5. "+crnl     
StrAdd(s5$,saryptr,4)

sptr=StrCat(iter)

endtm=(timer()-starttm) 
if endtm > 1000
	endtm=endtm / 1000
	units$="seconds"
else
	units$="milliseconds"
endif

dumpfile$="dump.txt"
deletefile(dumpfile$)
open to write 1,dumpfile$
write string 1,str$(endtm)+" "+units$
write string 1,peek string(sptr)
close file 1

delete memory sptr

end


function StrAdd(s$,inptr,idx)
	dim temp$(idx)
	link array temp$(),inptr
	temp$(idx)=s$
	tptr(idx)=get string ptr(temp$(idx))
	unlink array temp$()
	undim temp$()
endfunction

function StrCat(iter)
    tcnt=array count(tptr())
	tb=0
	for p=0 to tcnt
		t$=peek string(tptr(p))
 		inc tb, len(t$)
	next p
	
    tb = tb*iter
	
    cmem = alloc(tb+1) 
    fill memory cmem,0,tb
    lsz = 0
    for i=1 to iter
        for p=0 to tcnt
            if i=1 and p=0
                poke string cmem,peek string(tptr(p))
            else
                poke string cmem+lsz,peek string(tptr(p))
            endif
            inc lsz, len(peek string(tptr(p)))
        next p
    next i

	empty array strlist()
	empty array tptr()

endfunction cmem

//==============================================
// GENERAL STANDARD FUNCTIONS 
function DeleteFile(f$)
    if file exist(f$) then delete file f$
endfunction



Method 4 (with updated StrCat which uses link array command)

+ Code Snippet
Rem ***** Main Source File *****
sync on : sync rate 0 : sync
#constant crnl chr$(13)+chr$(10)    

dim strlist$(4) 
dim tptr(4) 
saryptr=get arrayptr(strlist$())
taryptr=get arrayptr(tptr())
`iter=1			` 0 milliseconds to process
`iter=100
`iter=500000	` 1 seconds to process
iter=1000000	` 2 seconds to process
`iter=2500000	` 5 seconds to process
`iter=5000000	` 10 seconds to process

timer()=0
starttm=timer()
s$="This is string 1. "
StrAdd(s$,saryptr,0)

s$="This is string 2. "
StrAdd(s$,saryptr,1)

s$="This is string 3. "
StrAdd(s$,saryptr,2)

s$="This is string 4. "
StrAdd(s$,saryptr,3)

s$="This is string 5. "+crnl     
StrAdd(s$,saryptr,4)

sptr=StrCat(taryptr,iter)

endtm=(timer()-starttm) 
if endtm > 1000
	endtm=endtm / 1000
	units$="seconds"
else
	units$="milliseconds"
endif

dumpfile$="dump.txt"
deletefile(dumpfile$)
open to write 1,dumpfile$
write string 1,str$(endtm)+" "+units$
write string 1,peek string(sptr)
close file 1

undim strlist$()
undim tptr()

end

function StrAdd(s$,inptr,idx)
	aryitems=get arrayptr count(inptr)
	if idx > aryitems 
		warningmessage "idx value parsed "+str$(idx)+" exceeds array upper limit number of items ("+str$(aryitems)+"). exiting!"
		exitfunction
	endif
	
	iptr=get arrayptr item ptr(inptr,idx)
	
	dim t$(idx)
	link array t$(),inptr
	t$(idx)=s$
	tptr(idx)=get string ptr(t$(idx))
	unlink array t$()
	undim t$()
endfunction

function StrCat(inptr,iter)
    tcnt=get arrayptr count(inptr)
    dim t(tcnt)
    link array t(),inptr
    
	tb=0
	for p=0 to tcnt
		inc tb, len( peek string(t(p)) )
	next p

    tb = tb*iter
	
    cmem = alloc(tb+1) 
    fill memory cmem,0,tb
    lsz = 0
    for i=1 to iter
        for p=0 to tcnt
            if i=1 and p=0
                poke string cmem,peek string(t(p))
            else
                poke string cmem+lsz,peek string(t(p))
            endif
            inc lsz, len(peek string(t(p)))
        next p
    next i

	unlink array t()
	undim t()
	
endfunction cmem

//==============================================
// GENERAL STANDARD FUNCTIONS 
function DeleteFile(f$)
    if file exist(f$) then delete file f$
endfunction

Posted: 23rd Feb 2019 12:22
Will be updating this soon to be more efficient and optimized and making use of link array etc

Will add functions back in once updated and tested thoroughly
Posted: 26th Feb 2019 5:10
added original code bavk in on first post with small tweaks for now. still testing the link array code version.
Posted: 26th Feb 2019 6:26
actually there is a command "GET STRING PTR" which can be used to return the pointer to a specified string variable, so might make another version implementing this command see if it has better results.
Posted: 27th Feb 2019 5:11
another method to do fast string concatenation using GET STRING PTR. Could also use data statements if you really wanted to to hold the strings depending on your scenario:



So this code now only uses the function FastConcatStrings and there are no memblocks or banks involved only MAKE MEMORY or you could use ALLOC. This example took 1.24 secs to concatenate 5 strings over 100000 iterations. 1 millions iterations takes about 12.6 secs on my laptop, will test on my desktop later. The traditional method will take minutes for 100000 iterations, a million iterations could take up to or even over an hour. Test it yourself if you want to. This faster concat approach is great for massive strings that you might want to export specific 3d model formats such as directX in ASCII format e,g, animation data strings in skinned meshes.
Posted: 28th Feb 2019 5:33
here is another method using link array and adding a AddConcatString function which adds more flexibility, this way you can parse any array pointer that holds the list of strings to be concatenated:
of course you can change function names and use any method you want depending on the scenario for your program. Running this code using 1 million iterations took 3.658 seconds to process. I commented out the code that does it the traditional way as your talking about over an hour or more probably. There is a limit I found i.e. trying 2 million iterations on 5 joined strings just bombed out on my laptop which doesn't have a great deal of memory so might be ok on my pc which has around 12 GB of memory or more, but reducing the number of strings to join to say 2 allowed the program to run it's course without prematurely exiting. 2 million iterations on 2 joined strings took 3.167 seconds.

Posted: 4th Mar 2019 13:11
tested on updated code not yet attached) using 10 million iterations on my desktop PC (64 bit windows 10) and took 10 seconds to process the concatenation of 3 short strings and about 79 seconds to actually print to screen.
Will probably apply latest code to my DBO to directX app and see if there is a massive improvement.
Posted: 7th Mar 2019 13:23
still updating this and testing. I've removed "fill memory" and used alloc zeroed function instead which fills the memory with 0's. Perfect for strings.
Posted: 8th Mar 2019 8:17
of course, you could just do away with all functions and do straight code, see example code below:

Posted: 22nd Aug 2021 14:04
reopened-will be adding all 3 methods plus hopefully improvements to the code.
Posted: 24th Aug 2021 7:02
added Method 4 code to the 1st post which is the shortest and more efficient. Uses only 2 functions StrAdd and StrCat. I had tried add to queue and various other alternative ways of doing it, but this way worked as I got odd results using add to queue. Do what you want with it , rename the functions to whatever you want. 5 million iterations took (on my laptop) 10 seconds to process, 1 million iterations took 2 seconds to process. doing this using traditional a$=a$+b$+c$ etc would take hours.to complete. I found exporting DBO to X ascii files using my conversion app was faster than modelling program e.g. Fragmotion.

I will of course be updating the StrCat function to use link array with an array pointer as I did with StrAdd function. So next update will be the last on this, at least for a while.

[update- StrCat function now updated and added code to 1st post (at bottom)]
[update 2-the StrCat function is actually slower with the link array than without by several seconds so it's your choice which one you use]
Posted: 25th Aug 2021 6:29
small update on 1st post to method 4 function StrAdd. Added warning message if index specified is above the array number of items in dimension
Posted: 25th Aug 2021 20:31
minor update to method 4 code : s1$-s5$ replaced with just s$, works fine.
Posted: 26th Aug 2021 6:29
updated Method 4 StrCat function: removed the "empty" array commands. Added undim's to end of main program. Undim Arrayptr (IanM matrix1util) command makes DBPro bomb out.
both StrAdd and StrCat functions now updated to use the link array ao you can parse any string array / temporary DWORD array pointers you want.

finding ways of optimising without using link array but nothing is working how I thought it would.