I've been using helpy's code for string building (viewtopic.php?t=12696&highlight=) but finally got frustrated when I could only call it once at a time. So I just ran myself through a quick course in pointers and, using froggerprogger's queue library as reference, created these string builder procedures that should allow you to use it any number of times. I also added a simple Length procedure.
Code: Select all
Structure StringBuilder
pString.i
StringSize.i
MemSize.i
BlockSize.i
InitDone.i
; Initialized to 0 (FALSE) at creation. Means by default
; we have not initialized the structure/class.
EndStructure
Procedure sbCreate(BlockSize)
;
Protected *sbClass.StringBuilder
;
*sbClass = AllocateMemory(SizeOf(StringBuilder))
;
If *sbClass\InitDone : FreeMemory(*sbClass\pString) : EndIf
; If the stringbuilder is already initialized, free the memory of the string.
; BlockSize min. 1024 ($400) Byte
If BlockSize < $400 : BlockSize = $400 : EndIf
; BlockSize max. 1 MByte ($100000) Byte
If BlockSize > $100000 : BlockSize = $100000 : EndIf
;
If BlockSize <> (BlockSize & $FC00)
BlockSize = (BlockSize & $FC00) + 1024
EndIf
;
*sbClass\BlockSize = BlockSize
*sbClass\StringSize = 0
*sbClass\pString = AllocateMemory(BlockSize)
; Allocate the memory needed for our string
If *sbClass\pString <> 0
; Allocation went fine, let the structure know we initialized everything fine.
*sbClass\InitDone = #True
*sbClass\MemSize = *sbClass\BlockSize
; Set our memory size used to the size of the block.
Else
; Problem with allocation - let it know we did not initialize
*sbClass\InitDone = #False
*sbClass\MemSize = 0
EndIf
;
ProcedureReturn *sbClass
; Return the pointer to our new stringbuilder class.
EndProcedure
Procedure sbClear(*inSBClass.StringBuilder)
;
If *inSBClass\InitDone
FreeMemory(*inSBClass\pString)
*inSBClass\pString = 0
*inSBClass\StringSize = 0
*inSBClass\BlockSize = 0
*inSBClass\InitDone = #False
*inSBClass\MemSize = 0
EndIf
;
EndProcedure
Procedure sbDestroy(*inSBClass.StringBuilder)
;
If *inSBClass\InitDone
FreeMemory(*inSBClass\pString)
*inSBClass\pString = 0
*inSBClass\StringSize = 0
*inSBClass\BlockSize = 0
*inSBClass\InitDone = #False
*inSBClass\MemSize = 0
EndIf
;
FreeMemory(*inSBClass)
;
EndProcedure
Procedure sbAdd(*inSBClass.StringBuilder, inString)
;
Protected StrLen
Protected pNewString
Protected NewMemSize
Protected NewStringSize
;
StrLen = MemoryStringLength(inString)
;
NewStringSize = StrLen + *inSBClass\StringSize
;
If NewStringSize + 1 > *inSBClass\MemSize
NewMemSize = *inSBClass\MemSize + *inSBClass\BlockSize
pNewString=AllocateMemory(NewMemSize)
If pNewString = 0 : ProcedureReturn #False : EndIf
CopyMemory(*inSBClass\pString, pNewString, *inSBClass\StringSize)
FreeMemory(*inSBClass\pString)
*inSBClass\pString = pNewString
*inSBClass\MemSize = NewMemSize
EndIf
;
CopyMemory(inString, *inSBClass\pString + *inSBClass\StringSize, StrLen)
*inSBClass\StringSize = NewStringSize
;
EndProcedure
Procedure sbAddLiteral(*inSBClass.StringBuilder, inString.s)
;
Protected sAddress
Protected StrLen
Protected pNewString
Protected NewMemSize
Protected NewStringSize
;
sAddress = @inString
StrLen = MemoryStringLength(sAddress)
;
NewStringSize = StrLen + *inSBClass\StringSize
;
If NewStringSize + 1 > *inSBClass\MemSize
NewMemSize = *inSBClass\MemSize + *inSBClass\BlockSize
pNewString=AllocateMemory(NewMemSize)
If pNewString = 0 : ProcedureReturn #False : EndIf
CopyMemory(*inSBClass\pString, pNewString, *inSBClass\StringSize)
FreeMemory(*inSBClass\pString)
*inSBClass\pString = pNewString
*inSBClass\MemSize = NewMemSize
EndIf
;
CopyMemory(sAddress, *inSBClass\pString + *inSBClass\StringSize, StrLen)
*inSBClass\StringSize = NewStringSize
;
EndProcedure
Procedure.s sbGetString(*inSBClass.StringBuilder)
;
Protected WholeString.s
;
WholeString = PeekS(*inSBClass\pString)
;
ProcedureReturn WholeString
;
EndProcedure
Procedure.s sbGetStringAndDestroy(*inSBClass.StringBuilder)
;
Protected WholeString.s
;
WholeString = PeekS(*inSBClass\pString)
sbDestroy(*inSBClass)
;
ProcedureReturn WholeString
;
EndProcedure
Procedure sbLength(*inSBClass.StringBuilder)
Protected iLength
iLength = *inSBClass\StringSize
ProcedureReturn iLength
EndProcedure
sHold.s = "1"
sDelimiter.s = ","
q = sbCreate(2048)
; Create a new string builder with a blocksize of 2048. You can create as many as you like. Just use a new long variable and pass it as a reference to the various functions.
sbAdd(q, @sHold)
; Add a string by the address of the string variable
sbAdd(q, @sDelimiter)
sbAddLiteral(q, "Hi!")
; Add by string rather than string address.
Debug sbGetString(q)
; Return the string itself
Debug "Length: "+Str(sbLength(q))
; Return the character count - 1 based. So "Hi" will return 2. Our example here should return 5.
sbDestroy(q)
; Destroy the string builder and clean up the used memory.
And thanks to helpy and froggerprogger for the code and examples
[Edit: I snuck in a "sbGetStringAndDestroy" procedure that will return the string and destroy the string builder. I'm not sure others will find this useful but I often need to return the built string as a procedurereturn so it's useful for me to return the string without setting the string builder to a string, destroying it and then returning the string]