Page 1 of 1

StringBuffer

Posted: Sun Feb 01, 2009 12:04 pm
by pcfreak
just see for yourself :P

Code: Select all

DisableDebugger 

Interface IStringBuffer
 add.i(String$)
 addC.i(Character.c)
 addUniC.i(Character.w)
 clear.i()
 get.s()
 getMem.i()
 copyToMem.i(*mem)
 length.i()
 ;free object
 delete()
EndInterface


Structure StringBufferElement
 *next.StringBufferElement
 *buffer
EndStructure


Structure OStringBuffer
 ;Address to the methods array
 methodAddress.i
 ;Class Attributes
 bufferSize.i
 bufferOffset.i
 *bufferList.StringBufferElement
 *lastBuffer.StringBufferElement
 length.i
EndStructure


Procedure.i StringBuffer_add(*this.IStringBuffer, String$)
 If *this = #Null
  ProcedureReturn #False
 EndIf
 *object.OStringBuffer = *this
 *buffer.StringBufferElement = *object\lastBuffer
 *in.CHARACTER = @String$
 stringSize.i = StringByteLength(String$)
 While *in - @String$ < stringSize
  If *object\bufferSize <= *object\bufferOffset
   *object\bufferOffset = 0
   *newBuffer = #Null
   While *newBuffer = #Null
    *newBuffer = AllocateMemory(*object\bufferSize)
   Wend
   *newBufferElement = #Null
   While *newBufferElement = #Null
    *newBufferElement = AllocateMemory(*object\bufferSize)
   Wend
   *buffer\next = *newBufferElement
   *buffer = *buffer\next
   *buffer\buffer = *newBuffer
   *object\lastBuffer = *buffer
  EndIf
  *out.WORD = *buffer\buffer + *object\bufferOffset
  *out\w = *in\c
  *object\bufferOffset + 2
  *in + SizeOf(CHARACTER)
  *object\length + 1
 Wend
 *object\lastBuffer = *buffer
 ProcedureReturn #True
EndProcedure


Procedure.i StringBuffer_addC(*this.IStringBuffer, Character.c)
 If *this = #Null
  ProcedureReturn #False
 EndIf
 *object.OStringBuffer = *this
 *buffer.StringBufferElement = *object\lastBuffer
 If *object\bufferSize <= *object\bufferOffset
  *object\bufferOffset = 0
  *newBuffer = #Null
  While *newBuffer = #Null
   *newBuffer = AllocateMemory(*object\bufferSize)
  Wend
  *newBufferElement = #Null
  While *newBufferElement = #Null
   *newBufferElement = AllocateMemory(*object\bufferSize)
  Wend
  *buffer\next = *newBufferElement
  *buffer = *buffer\next
  *buffer\buffer = *newBuffer
  *object\lastBuffer = *buffer
 EndIf
 *out.WORD = *buffer\buffer + *object\bufferOffset
 *out\w = Character
 *object\bufferOffset + 2
 *object\length + 1
 *object\lastBuffer = *buffer
 ProcedureReturn #True
EndProcedure


Procedure.i StringBuffer_addUniC(*this.IStringBuffer, Character.w)
 If *this = #Null
  ProcedureReturn #False
 EndIf
 *object.OStringBuffer = *this
 *buffer.StringBufferElement = *object\lastBuffer
 If *object\bufferSize <= *object\bufferOffset
  *object\bufferOffset = 0
  *newBuffer = #Null
  While *newBuffer = #Null
   *newBuffer = AllocateMemory(*object\bufferSize)
  Wend
  *newBufferElement = #Null
  While *newBufferElement = #Null
   *newBufferElement = AllocateMemory(*object\bufferSize)
  Wend
  *buffer\next = *newBufferElement
  *buffer = *buffer\next
  *buffer\buffer = *newBuffer
  *object\lastBuffer = *buffer
 EndIf
 *out.WORD = *buffer\buffer + *object\bufferOffset
 *out\w = Character
 *object\bufferOffset + 2
 *object\length + 1
 *object\lastBuffer = *buffer
 ProcedureReturn #True
EndProcedure


Procedure.i StringBuffer_clear(*this.IStringBuffer)
 If *this = #Null
  ProcedureReturn #False
 EndIf
 *object.OStringBuffer = *this
 *object\bufferOffset = 0
 *object\lastBuffer = *object\bufferList
 *buffer.StringBufferElement = *object\bufferList
 If *buffer\next
  *buffer = *buffer\next
  Repeat
   *nextBuffer = *buffer\next
   FreeMemory(*buffer\buffer)
   FreeMemory(*buffer)
   *buffer = *nextBuffer
  Until *buffer = #Null Or *buffer\next = #Null
 EndIf
 *object\bufferList\next = #Null
 *object\length = 0
 ProcedureReturn #True
EndProcedure


Procedure.s StringBuffer_get(*this.IStringBuffer)
 If *this = #Null
  ProcedureReturn ""
 EndIf
 *object.OStringBuffer = *this
 *buffer.StringBufferElement = *object\bufferList
 Result$ = ""
 While *buffer\next
  Result$ + PeekS(*buffer\buffer, *object\bufferSize >> 1, #PB_Unicode)
  *buffer = *buffer\next
 Wend
 Result$ + PeekS(*buffer\buffer, *object\bufferOffset >> 1, #PB_Unicode)
 ProcedureReturn Result$
EndProcedure


Procedure.i StringBuffer_getMem(*this.IStringBuffer)
 If *this = #Null
  ProcedureReturn #Null
 EndIf
 *object.OStringBuffer = *this
 *result = AllocateMemory((*object\length + 1) * 2)
 If *result = #Null
  ProcedureReturn #Null
 EndIf
 If *this\copyToMem(*result) = #Null
  ProcedureReturn #Null
 EndIf
 ProcedureReturn *result
EndProcedure


Procedure.i StringBuffer_copyToMem(*this.IStringBuffer, *mem)
 If *this = #Null
  ProcedureReturn 0
 EndIf
 *object.OStringBuffer = *this
 If *mem = #Null
  ProcedureReturn (*object\length + 1) * 2
 EndIf
 *buffer.StringBufferElement = *object\bufferList
 While *buffer\next
  CopyMemory(*buffer\buffer, *mem, *object\bufferSize)
  *mem + *object\bufferSize
  *buffer = *buffer\next
 Wend
 CopyMemory(*buffer\buffer, *mem, *object\bufferOffset)
 ProcedureReturn (*object\length + 1) * 2
EndProcedure


Procedure.i StringBuffer_length(*this.IStringBuffer)
 If *this = #Null
  ProcedureReturn -1
 EndIf
 *object.OStringBuffer = *this
 ProcedureReturn *object\length
EndProcedure


Procedure.i StringBuffer_delete(*this.IStringBuffer)
 If *this = #Null
  ProcedureReturn #False
 EndIf
 *this\clear()
 *object.OStringBuffer = *this
 FreeMemory(*object\bufferList\buffer)
 FreeMemory(*object\bufferList)
 FreeMemory(*object)
 ProcedureReturn #True
EndProcedure


DataSection
 OStringBuffer_methods:
  Data.i @StringBuffer_add()
  Data.i @StringBuffer_addC()
  Data.i @StringBuffer_addUniC()
  Data.i @StringBuffer_clear()
  Data.i @StringBuffer_get()
  Data.i @StringBuffer_getMem()
  Data.i @StringBuffer_copyToMem()
  Data.i @StringBuffer_length()
  Data.i @StringBuffer_delete()
EndDataSection


Procedure.i newStringBuffer(bufferSize.i = 128) ;KB
 If bufferSize < 1
  ProcedureReturn #Null
 EndIf
 *object.OStringBuffer = AllocateMemory(SizeOf(OStringBuffer))
 If *object = 0
  ProcedureReturn #Null
 EndIf
 *object\methodAddress = ?OStringBuffer_methods
 *object\bufferSize = bufferSize * 1024
 *object\bufferOffset = 0
 *object\bufferList = AllocateMemory(SizeOf(StringBufferElement))
 If *object\bufferList = #Null
  FreeMemory(*object)
  ProcedureReturn #Null
 EndIf
 *object\bufferList\next = 0
 *object\bufferList\buffer = AllocateMemory(*object\bufferSize)
 If *object\bufferList\buffer = #Null
  FreeMemory(*object\bufferList)
  FreeMemory(*object)
  ProcedureReturn #Null
 EndIf
 *object\lastBuffer = *object\bufferList
 *object\length = 0
 ProcedureReturn *object
EndProcedure


OpenConsole() 

Buf$ = "" 
t1 = ElapsedMilliseconds() 
For i = 0 To 10230 
 Buf$ + "aaaaaaaaaa" 
Next 
t1 = ElapsedMilliseconds() - t1 
l1 = Len(Buf$) 
Buf$ = "" 

b.IStringBuffer = newStringBuffer() 
t2 = ElapsedMilliseconds() 
For i = 0 To 10230 
 b\add("aaaaaaaaaa") 
Next 
Buf$ = b\get() 
t2 = ElapsedMilliseconds() - t2 
l2 = Len(Buf$) 
b\delete() 
Buf$ = "" 

PrintN("PureBasic: " + Str(t1) + " ms (" + Str(l1) + " characters)") 
PrintN("StringBuffer: " + Str(t2) + " ms (" + Str(l2) + " characters)") 

Input()
Edit: added some additional methods

Posted: Sun Feb 01, 2009 1:08 pm
by milan1612
I didn't take a closer look yet, but that looks really great! I always wanted to code
something like this, really useful for a lot of codes of mine! Thanks for sharing... :P

Posted: Sun Feb 01, 2009 1:30 pm
by Rings
a sort of Stringbuilder(.NET) for Pure.
Amazing work PCFreak .
specially if you increase stringsize,
your routines are very very fast.

Posted: Sun Feb 01, 2009 3:43 pm
by idle
bet that made you smile, much faster!

It's a good example of using OO too.

thanks for sharing.

Posted: Sun Feb 01, 2009 4:31 pm
by rsts
Elegant and nice :D

Thanks for sharing with us.

Posted: Mon Feb 02, 2009 12:37 am
by pdwyer

Code: Select all

For i = 0 To 10230
:o

Posted: Sun Feb 08, 2009 6:41 pm
by luis
Wow, very nice.

Thanks for sharing.

Posted: Sun Feb 08, 2009 11:08 pm
by Trond
You need to disable the debugger properly (from the menu) before doing speed tests.

Posted: Mon Feb 09, 2009 8:46 am
by Rings
Trond wrote:You need to disable the debugger properly (from the menu) before doing speed tests.
sure ?
pcfreak wrote:...

Code: Select all

DisableDebugger
.
.
.
[code][/quote]

Posted: Mon Feb 09, 2009 6:18 pm
by Motu
hi pcfreak,

great and speedy code, nice :-)

anyway, I guess there is a reason why PB does not handle strings that way. Change the end of your code to that:

Code: Select all

Global Buf.s = "" 
Global Test.s = ""
t1 = ElapsedMilliseconds() 
For i = 0 To 10230 
 Buf + "aaaaaaaaaa" 
 Test = Buf
Next 
t1 = ElapsedMilliseconds() - t1 
l1 = Len(Buf$) 
Buf$ = "" 

b.IStringBuffer = newStringBuffer() 
t2 = ElapsedMilliseconds() 
For i = 0 To 10230 
 b\add("aaaaaaaaaa") 
 Test = b\get() 
Next 
t2 = ElapsedMilliseconds() - t2 
l2 = Len(Buf$) 
b\delete() 
Buf$ = "" 

PrintN("PureBasic: " + Str(t1) + " ms (" + Str(l1) + " characters)") 
PrintN("StringBuffer: " + Str(t2) + " ms (" + Str(l2) + " characters)") 

and you'll see PB getting faster. You insert is really fast, but you have to pay the bill when calling the \get.

Posted: Mon Feb 09, 2009 9:48 pm
by Trond
Rings wrote:
Trond wrote:You need to disable the debugger properly (from the menu) before doing speed tests.
sure ?
pcfreak wrote:...

Code: Select all

DisableDebugger
.
.
.
[code][/quote][/quote]No, you need to disable the debugger from the menu or from the toolbar.

Posted: Wed Feb 11, 2009 9:38 am
by pcfreak
and you'll see PB getting faster. You insert is really fast, but you have to pay the bill when calling the \get.
true, but this code isn't to replace purebasic's string handling, it's just to build strings fast. in many cases you just add up small parts of strings to one string, if you make a html output from some database entries for example. just use it when needed :P