String Builder "Class"
Posted: Wed Nov 03, 2004 5:24 am
Code updated For 5.20+
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.
So you should be able to create and use as many string builders as you want. It'd be great if anyone could spot any way to update the code and make it faster or better. In fact, I'd appreciate if people could give it a shot. I'm just starting with pointers memory functions so I'm sure I messed something up.
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]
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]