StringBuilder Interface

Share your advanced PureBasic knowledge/code with the community.
User avatar
HeX0R
Addict
Addict
Posts: 1189
Joined: Mon Sep 20, 2004 7:12 am
Location: Hell

StringBuilder Interface

Post by HeX0R »

This is a rework of my module version from ->here<-, using an interface now for easier handling.
Since I somehow "hijacked" the thread from mk-soft (my apologize), I've created a new thread now.

Code: Select all

;- Top
; -----------------------------------------------------------------------------
;  Name        : StringBuilder Interface
;  Description : Fast String concatenation
;  Author      : HeX0R
;  Date        : 2023-02-16
;  Original    : https://www.purebasic.fr/english/viewtopic.php?p=558277#p558277
;  OS          : Cross-platform
;  Forum       : https://www.purebasic.fr/english/viewtopic.php?p=595972#p595972
;
; AND...ashes on my head!!:
; Base idea from mk-soft
;              : https://www.purebasic.fr/english/viewtopic.php?t=75758
; -----------------------------------------------------------------------------

Structure struc_StringBuilder
	VTable.i
	*Address
	*Pointer
	MemSize.i
	PacketSize.i
	Length.i
EndStructure

Interface IF_StringBuilder
	Add(String.s)
	GetString.s()
	Reset(KeepMemory = #False)
	Release()
EndInterface

Procedure __SB_Reset(*THIS.struc_StringBuilder, KeepMemory = #False)
	;Reset back to initial values, for reusing the interface
	If KeepMemory = #False
		If *THIS\MemSize > *THIS\PacketSize
			*THIS\Address = ReAllocateMemory(*THIS\Address, *THIS\PacketSize, #PB_Memory_NoClear)
		EndIf
		*THIS\MemSize = *THIS\PacketSize
	EndIf
	*THIS\Pointer = *THIS\Address
	*THIS\Length  = 0
EndProcedure

Procedure __SB_Add(*THIS.struc_StringBuilder, String.s)
	Protected Len, *Add, Result = #True
	
	Len = StringByteLength(String)
	If Len + *THIS\Length + 2 > *THIS\MemSize
		*THIS\MemSize = *THIS\Length + Len + 2 + *THIS\PacketSize
		*Add          = ReAllocateMemory(*THIS\Address, *THIS\MemSize, #PB_Memory_NoClear)
		If *Add
			If *Add <> *THIS\Address
				*THIS\Pointer = *THIS\Pointer - *THIS\Address + *Add
			EndIf
			*THIS\Address = *Add
		Else
			;not enough memory?
			ProcedureReturn #False
		EndIf
	EndIf
	CopyMemoryString(@String, @*THIS\Pointer)
	*THIS\Length + Len
	
	ProcedureReturn Result
EndProcedure

Procedure.s __SB_GetString(*THIS.struc_StringBuilder)
	ProcedureReturn PeekS(*THIS\Address, *THIS\Length / SizeOf(Character))
EndProcedure

Procedure __SB_Release(*THIS.struc_StringBuilder)
	If *THIS\Address
		FreeMemory(*THIS\Address)
	EndIf
	FreeMemory(*THIS)
EndProcedure

Procedure New_StringBuilder(PacketSize.i = 65536)
	Protected *THIS.struc_StringBuilder, *Buffer
	
	*THIS        = AllocateMemory(SizeOf(struc_StringBuilder))
	*THIS\VTable = ?_VT_STRINGBUILDER_
	If *THIS = 0
		ProcedureReturn #False
	EndIf
	
	*Buffer = AllocateMemory(PacketSize, #PB_Memory_NoClear)
	If *Buffer
		*THIS\Address    = *Buffer
		*THIS\Pointer    = *Buffer
		*THIS\PacketSize = PacketSize
		*THIS\MemSize    = PacketSize
		ProcedureReturn *THIS
	Else
		FreeMemory(*THIS)
	EndIf
	ProcedureReturn #False
	
	DataSection
		_VT_STRINGBUILDER_:
		Data.i @__SB_Add()
		Data.i @__SB_GetString()
		Data.i @__SB_Reset()
		Data.i @__SB_Release()
	EndDataSection
EndProcedure

;----------------------------EOF-------------------------


CompilerIf #PB_Compiler_IsMainFile
	
	Define i, a$, b$, c$, s1, s2
	Define SB.IF_StringBuilder
	
	SB = New_StringBuilder()
	a$ = "abcdefghijklmnopqrstuvwxyz" + #CRLF$
	s1 = ElapsedMilliseconds()
	For i = 1 To 10000
		SB\Add(a$)
	Next i
	b$ = SB\GetString()
	s1 = ElapsedMilliseconds() - s1
	s2 = ElapsedMilliseconds()
	For i = 1 To 10000
		c$ + a$
	Next i
	s2 = ElapsedMilliseconds() - s2
	MessageRequester("Info", ~"Stringbuilder: \t" + Str(s1) + ~"ms\nPB-Way: \t" + Str(s2) + "ms")
	;Debug b$
	SB\Release()
	
CompilerEndIf
Last edited by HeX0R on Fri Feb 17, 2023 10:15 pm, edited 2 times in total.
User avatar
ChrisR
Addict
Addict
Posts: 1466
Joined: Sun Jan 08, 2017 10:27 pm
Location: France

Re: StringBuilder Interface

Post by ChrisR »

Great, the interface really makes it easier to handle 8)
I reworked a bit on the base of your previous module, without the interface.
In order not to "hijack" your thread or mk-soft's one, I will post it in a new topic.
So one more, sorry, but there may be things to take.
Thank you for this rework, waiting for a native solution for large strings :wink:
User avatar
StarBootics
Addict
Addict
Posts: 1006
Joined: Sun Jul 07, 2013 11:35 am
Location: Canada

Re: StringBuilder Interface

Post by StarBootics »

@HeXOR : when you allocate a structure with the AllocateStructure() it's better to use FreeStructure() for consistency not FreeMemory(). Beside that nice job !

Best regards
StarBootics
The Stone Age did not end due to a shortage of stones !
User avatar
HeX0R
Addict
Addict
Posts: 1189
Joined: Mon Sep 20, 2004 7:12 am
Location: Hell

Re: StringBuilder Interface

Post by HeX0R »

That's true, but where exactly did I use AllocateStructure()?
There are no lists in those structures, therefore an AllocateMemory() is good enough
User avatar
NicTheQuick
Addict
Addict
Posts: 1504
Joined: Sun Jun 22, 2003 7:43 pm
Location: Germany, Saarbrücken
Contact:

Re: StringBuilder Interface

Post by NicTheQuick »

HeX0R wrote: Fri Feb 17, 2023 10:24 amThat's true, but where exactly did I use AllocateStructure()?
If I don't want to allocate only a certain amount of bytes, but there is an underlying structure, then I always use AllocateStructure(). This way I am also prepared for future changes to the structure.
Or does that create some overhead that I don't know about (yet)?
The english grammar is freeware, you can use it freely - But it's not Open Source, i.e. you can not change it or publish it in altered way.
User avatar
Mijikai
Addict
Addict
Posts: 1517
Joined: Sun Sep 11, 2016 2:17 pm

Re: StringBuilder Interface

Post by Mijikai »

Thx for sharing HeX0R.
User avatar
mk-soft
Always Here
Always Here
Posts: 6204
Joined: Fri May 12, 2006 6:51 pm
Location: Germany

Re: StringBuilder Interface

Post by mk-soft »

HeX0R wrote: Thu Feb 16, 2023 10:46 pm This is a rework of my module version from ->here<-, using an interface now for easier handling.
Since I somehow "hijacked" the thread from mk-soft (my apologize), I've created a new thread now.
If already, then already take over everything. :mrgreen:

Missing concat, left, right, insert, len :wink:

and missing "base of mk-soft" :cry:
My Projects ThreadToGUI / OOP-BaseClass / EventDesigner V3
PB v3.30 / v5.75 - OS Mac Mini OSX 10.xx - VM Window Pro / Linux Ubuntu
Downloads on my Webspace / OneDrive
User avatar
HeX0R
Addict
Addict
Posts: 1189
Joined: Mon Sep 20, 2004 7:12 am
Location: Hell

Re: StringBuilder Interface

Post by HeX0R »

Well... this is a stringbuilder... nothing else!
No need for any more fancy things like left, right, len, whatever.
It is to overcome the bottleneck of PBs string handling for concatenation very long strings, and who in the world would start to do a Rset() or Insert (and why??) in a several thousand bytes long string?
Regarding the AllocateMemory() against the AllocateStructure() discussion:
In the code above it doesn't really matter, although, all your remarks are valid!
But there never will be any list, array or map in that structure (again: it is nothing but a simple stringbuilder), therefore there is no real difference between both variants.
and missing "base of mk-soft" :cry:
You are abolutely right, my apologize, I'll add that in a minute!
User avatar
ChrisR
Addict
Addict
Posts: 1466
Joined: Sun Jan 08, 2017 10:27 pm
Location: France

Re: StringBuilder Interface

Post by ChrisR »

hmm, I understand your point and I thought like you.
But I started to use it in my app and I have 3 functions that I need, copied from my Fork:
- Concat: to concatenate more quickly multiple stringbuilder
- Right: in few cases, I need to know if the string ends with a double #CRLF$ or not, and it's simple and fast here: *Pointer - Length
- Insert: to be able to insert at the beginning of the string, although I could do it differently but it's faster here with Move and CopyMemory
I don't need anything else right now
User avatar
skywalk
Addict
Addict
Posts: 4211
Joined: Wed Dec 23, 2009 10:14 pm
Location: Boston, MA

Re: StringBuilder Interface

Post by skywalk »

Thanks for the post.
I know this is nitpicking, but a true comparison should return the string to a PB variable for later use.
b$ = SB\GetString()
And b$ = #Empty$ before PB way runs.
The nice thing about standards is there are so many to choose from. ~ Andrew Tanenbaum
User avatar
HeX0R
Addict
Addict
Posts: 1189
Joined: Mon Sep 20, 2004 7:12 am
Location: Hell

Re: StringBuilder Interface

Post by HeX0R »

Not nitpicking, but valid!
I've added it for completeness.

@Chris:
All you've said is true, but the definition of a "stringbuilder" is nothing else, then what is included above.
Since yours is called ExString (and the one from mk-soft FastString), there is nothing wrong to add some more features to it.
I personally needed a stringbuilder only once, for a cgi script that builds-up pretty huge HTML output.
There was no need to do any more stuff on that huge string, but there might come a time, where I need to do more things, then I definately will look into your or mk-softs interpretations.
But in all "typical applications", I'm fine with PBs string performance.
Post Reply