byte ptr to string

Started by Sinjin, May 22, 2024, 23:59:30

Previous topic - Next topic

Sinjin

I try to initialize strings with a given size. I tried the following but it doesnt work. Im not sure how blitz handles strings. It appear to work, but I guess the system dont recognise that as an actual string.
'test.s
format MS COFF
section "code" code align 4
;out eax string
;in esp+4 byte ptr
public _byteptr2str
_byteptr2str:
  mov eax,[esp+4]
ret

'main.bmx
import "test.s"

print initstring(asc"a",10)
input

extern
  function byteptr2str$(bptr:byte ptr)
endextern

function initstring$(ch%,ln%)
  local bp:short ptr=short ptr memalloc(12+ln shl 1) '2 bytes per char
  int ptr(bp)[0]=$454060    '?
  int ptr(bp)[1]=$80000000  'flags? $FFFFFF7F=const, $80000000=initialized even with ="", $40000004=not initialized
  int ptr(bp)[2]=ln
  for local a%=0 until ln
    bp[a+6]=ch
  next
  return byteptr2str(bp)
endfunction

Midimaster

#1
Will you use this in BlitzMax MaxGui? Or old BlitzBasic?
What is your reason for doing this with an external function?

In BlitzMax you can do this with slices:

    Local a:String = ""[0..999]
...produces a String with a length of 999 characters

And this is a speed test:

Global TimeStamp:Int = MilliSecs()
For Local i:Int=0 To 999
    CreateSliceString
Next
Print "1000 Strings via  SLICES: " + (MilliSecs()-TimeStamp) + " msec"

TimeStamp=MilliSecs()
For Local i:Int=0 To 999
    CreateClassicString
Next
Print "1000 Strings via ADDDING: " + (MilliSecs()-TimeStamp) + " msec"

Function CreateSliceString()
    Local a:String = ""[0..999]
End Function

Function CreateClassicString()
    Local a:String =""
    For Local i:Int=0 To 999
        a= a + " "
    Next
End Function

returns
...
1000 Strings via  SLICES: 13 msec
1000 Strings via ADDDING: 1028 msec

So Slices are 75 times faster than adding characters.

The Slices produces Strings with SPACE characters. If you want a certain ASCII you would add this:

Function CreateSliceString()
Local a:String = ""[0..999]
a.Replace(" ", "x")
End Function

Speed is now 22msec for 1000. Means: still 45 times faster than ADDING.

A ready to use function:

Function CreateString:String(length:Int, Char:String)
Local Fix:String = ""[0..length]
Fix.Replace(" ", Char)
Return Fix
End Function
...back from North Pole.

Sinjin

#2
Thanks, I didnt know it was possible that way. I have some functions where I add slices to a string or simply add other strings and every time it adjusts the internal memory. Now i can simply override chars and the memory is already allocated.
Now I can replace the replace function with a much faster one :)

Sinjin

Strangely enough my old filling function is still faster than ""[..ln] even without the replace.
function strmul$(ch$,mul%)
  if not mul or not ch then return ""
  mul:*ch.length
  local a%=ch.length
  while (a+a<mul)
    ch:+ch
    a:+a
  wend
  return ch+ch[..mul-a]
endfunction

Midimaster

#4
Wow! The combination is the winner. You combined SLICES with a doubling algorithm. This could be a ready to use BlitzMax function for creating strings with a given length:

Function CreateSpaceString:String(length:Int, Char:String=" ")
    ' Returns a string with given length.
    ' additional you can define the fill character. Default is SPACE
    
    If length=0 Then Return ""
    Local Fix:String = Char[..1]
    Local size:Int = 1   
    While (size+size)<length
        size :+ size
         Fix :+ Fix
    Wend
    Fix :+ Fix[..(length-size)]
    Return Fix 
End Function
...back from North Pole.

Sinjin

#5
Here is another function that is ~4 times faster now if the number is over millions. I do the speed test a bit different, I find mine more accurate, and I was able to make my strmul function even a bit faster but only if you use more than 1 char in assembler too(didnt post it). Besides.. don't ask, im going over all my functions right now, I want everything still a little faster...it's work :)

'test.s
format MS COFF
section "code" code align 4
;inout esp+4 string
;in esp+8 pos
;in esp+12 overwrite string
public _overright
_overright:
  mov edx,[esp+4] ;string
  mov ecx,[edx+8] ;length
  mov eax,[esp+8] ;pos
  sub ecx,eax ;if string.length-pos<=0
 jbe .out
  add edx,eax
  add edx,eax
  add edx,12
   push edi
  mov edi,[esp+4+12] ;over
  cmp ecx,[edi+8] ;if string.length-pos>=over.length then ecx=over.length
 jc .g0
  mov ecx,[edi+8]
.g0:
  add edi,12

  shr ecx,1 ;copy short
 jnc .g1
  mov ax,[edi]
  mov [edx],ax
  add edi,2
  add edx,2
.g1:
  or ecx,ecx
 jz .outok

.j0:
  mov eax,[edi] ;copy int
  mov [edx],eax
  add edi,4
  add edx,4
  dec ecx
 jnz .j0
.outok:
   pop edi
.out:
ret

;inout esp+4 string
;in esp+8 pos [1..]
;in esp+12 overwrite string
public _overleft
_overleft:
  mov edx,[esp+4] ;string
  mov eax,[esp+8] ;pos
  cmp eax,[edx+8] ;if pos<string.length
 ja .out
  add edx,eax
  add edx,eax
  add edx,12
   push edi
  mov edi,[esp+4+12] ;over
  mov ecx,[edi+8]
  add edi,ecx
  add edi,ecx
  add edi,12
  cmp ecx,eax ;if over.length>=pos then ecx=pos
 jb .g0
  mov ecx,eax
.g0:

  shr ecx,1 ;copy short
 jnc .g1
  sub edi,2
  sub edx,2
  mov ax,[edi]
  mov [edx],ax
.g1:
  or ecx,ecx
 jz .outok

.j0:
  sub edi,4 ;copy int
  sub edx,4
  mov eax,[edi]
  mov [edx],eax
  dec ecx
 jnz .j0
.outok:
   pop edi
.out:
ret

;------------------------------------------------------------

'main.bmx
print pointed("1234567",",",3)+":"
input

'speed-test
repeat
  local cn%,tm%=millisecs()
  repeat
pointed "1234567",".",3
;pointed_old "1234567",".",3
    cn:+1
  until (millisecs()-tm>=1000)
  print pointed(cn)
forever

extern
'  function byteptr2str$(bptr:byte ptr)
  function overright(src$,pos%,ch$)
  function overleft(src$,pos%,ch$)
endextern

function pointed$(src$,ch$=",",stp%=3)
  if not stp or (src.length<=stp) then return src
  local oln%=src.length
  src:+""[..(src.length-1)/stp*ch.length]
  local p%=src.length
  while (p>stp)
    oln:-stp
    overleft src,p,src[oln..oln+stp]
    p:-stp'+ch.length
    overleft src,p,ch
    p:-ch.length
  wend
  return src
endfunction

function pointed_old$(src$,ch$=".",inter%=3)
  local p%=src.length
  repeat
    p:-inter
    if (p>0) then src=src[..p]+ch+src[p..] else return src
  forever
endfunction

Sinjin

#6
And to correct myself, IT IS FASTER, despite what I said in my 3rd comment, I kinda have 3 files and 5 functions and compile yet another file...of course ""[..x] is super fast :D and again, I always thought exceeding array sizes exeeds boundaries. I never read the whole docs, but im sure its not in it. After all you could say that once you exeeds constants, it will allocate new memory for strings.
Again, super great knowledge you have midimaster!!! I almost wonder if you know asm, but I know you know very crude asm : D ,its not always a set back...I meet ppl who claim to know EVERYTHING about AI, and they might be able to code it, but they dont know what a matrix is  lol...but you dont have to know the computer starts in real-mode....we all code in 32/64 bits :D  ppls dont see downwards..where it comes from. Once 18, you have to earn money...NOT invent new stuff!... in fact PLS DONT! hahahaha Ppl get "forced" to adjust their papers! OMG! NEVER admit to a bug!!! haha
Not bugs but HOW DARE you challenge speeds in asm...then I hear use SIMD commands....but INSTR or any string related stuff dont! Not system/bmax wide! HOW DARE AM I??? haha, all good :D I do MY functions faster and better, even if I dont use those new stuff. Plus using new stuff...I mean in asm, whats FS and GS for nowadays? i would liked t better to have more CPU registers, and I know which 64bits brings..... Im not there yet lol I cant run blitzng ide....who cares? im just one guy (no money=not important)! so i code what i can code, still its getting faster, so im good again haha jeeeeee
Tomorrow its not SIMD any more but SIME, one letter further! haha crap new stuff! Sometimes they think to give a new version number instead of making it for the future.
MOVSD, STOSD is slower than actually code it?
.j0:
stosd
loop .j0

is slower than
.j0:
mov eax[esi]
mov [edi],eax
add edi,2
add esi,2
dec ecx
jnz .jo


why is such command a thing?

Sinjin

#7
In a fact im happy, bc all this triggers me to go all back down, to make it all faster! NO MORE POP AND PUSHES  lol...DIRECTY write the address where it has to go is the fututre:
MOV EDI,string
MOV ESI,outstring
call stringcopy
;in edi source
;out esi destination
no push edi and then mov edi,[esp+4]...
directly in EDI!
Only the compiler have to keep more track!  no AI needed!
besides, there cant be another operand to be EDI in it....in your function you write somwhere: mov edi,[esp+4].....
why push it in the 1st place?
If your function claims a sting top be in EDI and later uses it from the start, in the function itself i would do a push, but before a call, i would do pushes quite seldom!
HOW DARE CAN I BE?  lol
Then I wonder how dare I am and be even able to make it faster????
WE ALL make it faster, other ppl just have to embrace it!

Sinjin

#8
For windows for example, instead of fiddling how the sizes and positions work I do this now:
private
extern "Win32"
  function getwindowplacement%(hwnd%,wp@ptr)="GetWindowPlacement@8"
  function setwindowplacement%(hwnd%,wp@ptr)="SetWindowPlacement@8"
endextern
public

'------------------------------------------

private
type twindowplacement
  field ln%=sizeof(twindowplacement)
  field flags%
  field showcmd% '1=normal,2=minimized,3=maximized
  field mnx%,mny% 'ptminposition%[2]
  field mxx%,mxy% 'ptmaxposition%[2]
  field x1%,y1%,x2%,y2% 'rcnormalposition%[4]

  method load(f:tstream)
    flags=readbyte(f)
    showcmd=flags&$3
    flags:shr 2
    x1=readuint(f)
    y1=readuint(f)
    x2=readuint(f)
    y2=readuint(f)
    mnx=readsint(f)
    mny=readsint(f)
    mxx=readsint(f)
    mxy=readsint(f)
  endmethod

  method save(f:tstream)
    writebyte f,showcmd+flags shl 2
    writeuint f,x1
    writeuint f,y1
    writeuint f,x2
    writeuint f,y2
    writesint f,mnx
    writesint f,mny
    writesint f,mxx
    writesint f,mxy
  endmethod

  method get(win:tgadget)
    getwindowplacement querygadget(win,query_hwnd),self
  endmethod

  method set(win:tgadget)
    setwindowplacement querygadget(win,query_hwnd),self
  endmethod
endtype
public
then you just GET and SET it, and then you have a window that remains as is was as you left it!
no more fiddling around what blitz gives you!
for write/readuint or write/readsint, just use writeint and it'll be fine.