Page 1 of 1

ReverseString() and ReverseMemory(), v1.1

Posted: Tue Oct 07, 2008 11:42 pm
by Rescator
As usual, I was working on something, I needed a ReverseString() function but PureBasic has none. So i wrote one. Which led to more code, and eventually this suite of reverse functions.

ReverseString()
which returns a new reversed string.

and these which does in-memory reversing.

ReverseMemory() variants:
ReverseMemoryB() ;Reverse a memory area of byte.
ReverseMemoryW() ;Reverse a memory area of word.
ReverseMemoryL() ;Reverse a memory area of long.
ReverseMemoryQ() ;Reverse a memory area of quad.
ReverseMemoryF() ;Reverse a memory area of float.
ReverseMemoryD() ;Reverse a memory area of double.
ReverseMemoryI() ;Reverse a memory area of integer.
ReverseMemoryC() ;Reverse a memory area of char.

As ReverseMemoryC() works on characters it can be used used as a in-memory string reversion as well, make sure you give it the real memory length rather than string length though.


Here is the code along with some quick tests.
ReverseMemory() should be quite fast, they are all basically the same, and in fact Double and Float are just macros re-using the Quad and Long code as Double and Quad, and Float and Long both use the same amount of memory for it's storage.

ReverseString() should be fast. (it uses the ReverseMemoryC() code after all) but with some extra features. Like optional start and length parameters that let you do some more fancy string reversing stuff.
I'm sure ReverseString() can be enhanced/improved a bit more, but I'll leave that to some of you other crazy people on the forum as I need to get back to what I was originally coding on, instead of these cute little functions. (PS! I'll also be requesting ReverseString() and ReverseMemory() in the features forum, hopefully a future PureBasic version will have these in native code for even more speed and features.)


This source is Public Domain.

Code: Select all

EnableExplicit

Procedure ReverseMemoryB(*memory.Byte,length.i)
 Protected *memend.Byte
 If length>SizeOf(Byte)
  *memend=(*memory+length)-SizeOf(Byte)
  length>>1
  While length>0
   Swap *memory\b,*memend\b
   *memory+SizeOf(Byte)
   *memend-SizeOf(Byte)
   length-SizeOf(Byte)
  Wend
 EndIf
EndProcedure

Procedure ReverseMemoryW(*memory.Word,length.i)
 Protected *memend.Word
 If length>SizeOf(Word)
  *memend=(*memory+length)-SizeOf(Word)
  length>>1
  While length>0
   Swap *memory\w,*memend\w
   *memory+SizeOf(Word)
   *memend-SizeOf(Word)
   length-SizeOf(Word)
  Wend
 EndIf
EndProcedure

Procedure ReverseMemoryL(*memory.Long,length.i)
 Protected *memend.Long
 If length>SizeOf(Long)
  *memend=(*memory+length)-SizeOf(Long)
  length>>1
  While length>0
   Swap *memory\l,*memend\l
   *memory+SizeOf(Long)
   *memend-SizeOf(Long)
   length-SizeOf(Long)
  Wend
 EndIf
EndProcedure

Procedure ReverseMemoryQ(*memory.Quad,length.i)
 Protected *memend.Quad
 If length>SizeOf(Quad)
  *memend=(*memory+length)-SizeOf(Quad)
  length>>1
  While length>0
   Swap *memory\q,*memend\q
   *memory+SizeOf(Quad)
   *memend-SizeOf(Quad)
   length-SizeOf(Quad)
  Wend
 EndIf
EndProcedure

Macro ReverseMemoryF(memory,length)
 ReverseMemoryL(memory,length)
EndMacro

Macro ReverseMemoryD(memory,length)
 ReverseMemoryQ(memory,length)
EndMacro

Procedure ReverseMemoryI(*memory.Integer,length.i)
 Protected *memend.Integer
 If length>SizeOf(Integer)
  *memend=(*memory+length)-SizeOf(Integer)
  length>>1
  While length>0
   Swap *memory\i,*memend\i
   *memory+SizeOf(Integer)
   *memend-SizeOf(Integer)
   length-SizeOf(Integer)
  Wend
 EndIf
EndProcedure

Procedure ReverseMemoryC(*memory.Character,length.i)
 Protected *memend.Character
 If length>SizeOf(Character)
  *memend=(*memory+length)-SizeOf(Character)
  length>>1
  While length>0
   Swap *memory\c,*memend\c
   *memory+SizeOf(Character)
   *memend-SizeOf(Character)
   length-SizeOf(Character)
  Wend
 EndIf
EndProcedure

Procedure.s ReverseString(string$,start.i=1,length.i=0) ;Ascii and Unicode. To be consistent with other string functions this makes a new string rather than in-place swapping.
 Protected newstring$,*memory.Character,*memend.Character,len.i
 start-1
 If start<0
  start=0
 EndIf
 len=Len(string$)
 If length<1
  length=len-start
 EndIf
 If (length+start)>len
  length=len
 EndIf
 len=length
 newstring$=string$
 If len>1
  start*SizeOf(Character)
  len*SizeOf(Character)
  *memory=@newstring$+start
  *memend=(*memory+len)-SizeOf(Character)
  len>>1
  While len>0
   Swap *memory\c,*memend\c
   *memory+SizeOf(Character)
   *memend-SizeOf(Character)
   len-SizeOf(Character)
  Wend
 EndIf
 ProcedureReturn newstring$
EndProcedure

Define text$

;As this reverses the memory using Character, this will work in both ascii and unicode.
;Warning! All ReverseMemory() functions are in-place, ReverseString() might be a better choice in most cases.
text$="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
ReverseMemoryC(@text$,StringByteLength(text$))
Debug "ReverseMemory: "+text$

;Reverse the whole string.
text$="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
Debug "ReverseString all: "+ReverseString(text$)

;Reverse from A to Q only.
text$="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
Debug "ReverseString middle: "+ReverseString(text$,11,17)

;Reverse from A to Q only, then reverse all, resulting in a inversion so the middle is "non-reversed".
text$="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
Debug "ReverseString ends: "+ReverseString(ReverseString(text$,11,17))
v1.0 - Tue Oct 07, 2008

v1.1 - Sat Oct 10, 2008
Fixed procedure name copy/paste screwup.
Fixed the while loops to use greater than comparison, stupid mistake but oh so easy to forget.

Posted: Wed Oct 08, 2008 12:05 pm
by Trond
This function is broken in unicode mode:

Code: Select all

Procedure ReverseMemoryC(*memory.Character,length.i) 
 Protected *memend.Character 
 If length>SizeOf(Character) 
  *memend=(*memory+length)-SizeOf(Character) 
  length>>1 
  While length 
   Swap *memory\c,*memend\c 
   *memory+SizeOf(Character) 
   *memend-SizeOf(Character) 
   length-SizeOf(Character) 
  Wend 
 EndIf 
EndProcedure 

Define text$ 
text$="123456789"
ReverseMemoryC(@text$,StringByteLength(text$)) 
Debug "ReverseMemory: "+text$
And why not make ReverseString() use ReverseMemoryC()?

Code: Select all

Procedure.s ReverseString(string$,start.i=1,length.i=0) ;Ascii and Unicode. To be consistent with other string functions this makes a new string rather than in-place swapping.
  Protected newstring$ = string$
  If length = 0
    length = StringByteLength(newstring$)
  EndIf
  ReverseMemoryC(@newstring$+start*SizeOf(Character)-SizeOf(Character), length)
  ProcedureReturn newstring$
EndProcedure

Posted: Wed Oct 08, 2008 12:30 pm
by nco2k
@Rescator
you switched accidentally ReverseMemoryL() and ReverseMemoryW().

c ya,
nco2k

Posted: Sat Oct 11, 2008 12:19 am
by Rescator
Trond wrote:This function is broken in unicode mode:
ReverseMemoryC()
Damn, n00b'like screwup, shame on me. The while loop was using "if equal" instead of "if greater than". Fixed, thanks! ;) (error was due to odd sizes making the pointer overrun, ugh)
Trond wrote:And why not make ReverseString() use ReverseMemoryC()?
I could have, but that would have meant the procedure would call another procedure, which means extra overhead.
So I went for speed (duplicate and adapted code for the string procedure) rather than a few lines shorter.

Not sure how much more the memory footprint is for that procedure but I doubt it's more than half a K? And avoiding that extra procedure call overhead is worth it in my opinion.
In fact I'm sure the ReverseString() procedure can be sped up some more even.
Edit: tossing out the bounds checks for start and length as you did also speed it up, but I would not recommend that. Also the bounds checks does not support negative start or length so I guess it could be improved further to handle that as well so one could reference from the end instead.
nco2k wrote:you switched accidentally ReverseMemoryL() and ReverseMemoryW().
Oh crap, perils of copy'n'paste and assuming they work. The byte procedure was the initial one. Fixed, thanks :)