Page 1 of 2

Using adresses as a key for AddMapElements ???

Posted: Sat Nov 23, 2019 4:00 pm
by Joris
Hi,

Is there a save and fast way to use adresses as a key for maps like this :
AddMapElement(map(), Str(adres))
Chr(47710280) is faster as Str(47710280) but does only two bytes.
And ... how to convert back ?
Val(MapKey(map())) ;<= Converts a string into a quad numeric value.

Thanks.

Re: Using adresses as a key for AddMapElements ???

Posted: Thu Dec 12, 2019 7:32 pm
by Fig
The only fast and safe way is to use a custom hastable library that accepts numbers as key.
But it depends how fast you need to go, Pb map are pretty good even if str(adress) is somehow slow.

Remark, your adress evolves probably 4 by 4 so you can reduce its key by right shifting it by 2.

Re: Using adresses as a key for AddMapElements ???

Posted: Fri Dec 13, 2019 2:02 am
by idle
the pb map header suggests there is a numeric map key, this works on linux x64

LINUX x64

Code: Select all

ImportC ""
  PB_AddNumericMapElement(*Map,key.i);
  PB_FindNumericMapElement(*Map,Key.i);
EndImport 

Global NewMap dummy.i() 

 Macro _NewMap(var,size=512) ;linux x64 
   EnableASM   
   mov    rdi,size     
   !MOV    r8,rdi
   lea    rcx, var     ;pointer 
   !XOr    rdx,rdx
   !MOV    rsi,21        
   !MOV    rdi,8          
   !SUB    rsp,32       
   !CALL   PB_NewMap
   !ADD    rsp,32
   DisableASM 
 EndMacro 
   
Global *mp 
_NewMap(*mp,1024)  

Global *el.Integer 
; 
*el = PB_AddNumericMapElement(*mp,123) 
*el\i = 123 
; 
*el = PB_AddNumericMapElement(*mp,345) 
*el\i = 345 
; 
*el = PB_FindNumericMapElement(*mp,123) 
Debug *el\i 
*el = PB_FindNumericMapElement(*mp,345) 
Debug *el\i 

Re: Using adresses as a key for AddMapElements ???

Posted: Fri Dec 13, 2019 10:02 am
by Joris
idle wrote:the pb map header suggests there is a numeric map key...
What do you mean with this ? I see only strings as map key in the PB-helpfiles.

Your code doesn't works on windows, so ... no can do.

Thanks.

Re: Using adresses as a key for AddMapElements ???

Posted: Fri Dec 13, 2019 10:12 am
by idle
Joris wrote:
idle wrote:the pb map header suggests there is a numeric map key...
What do you mean with this ? I see only strings as map key in the PB-helpfiles.

Your code doesn't works on windows, so ... no can do.

Thanks.
The c headers for maps say it all

PB_NewMap(integer ElementSize, int ElementType, integer *StructureMap, PB_Map **Address, int HashSize);
PB_NewNumericMap(integer ElementSize, integer *StructureMap, PB_Map **Address, int HashSize);

I don't know how long it's been like that but it is clearly an option which just isn't exposed.

Hey, I did clearly say it only worked on Linux x64, I ran out of time to look at it, however Wilbert has taken a look and I expect he will post a solution soon!

Re: Using adresses as a key for AddMapElements ???

Posted: Fri Dec 13, 2019 11:17 am
by wilbert
Joris wrote:Your code doesn't works on windows, so ... no can do.
Idle means the procedures are there but they aren't exposed so you won't find them in the help file.

Here's an example ...

Code: Select all

;->> Map extension module <<

DeclareModule MapEx
   
  Prototype GetMapPointerI (Map IntegerMap.i())
  Prototype GetMapPointerL (Map LongMap.l())
  Prototype GetMapPointerQ (Map QuadMap.q())
  Prototype GetMapPointerS (Map StringMap.s())
  
  Prototype NumericMapKeyI (Map IntegerMap.i())
  Prototype NumericMapKeyL (Map LongMap.l())
  Prototype NumericMapKeyQ (Map QuadMap.q())
  Prototype NumericMapKeyS (Map StringMap.s())
  
  Global GetMapPointerI.GetMapPointerI
  Global GetMapPointerL.GetMapPointerL
  Global GetMapPointerQ.GetMapPointerQ
  Global GetMapPointerS.GetMapPointerS
  
  Global NumericMapKeyI.NumericMapKeyI
  Global NumericMapKeyL.NumericMapKeyL
  Global NumericMapKeyQ.NumericMapKeyQ
  Global NumericMapKeyS.NumericMapKeyS
  
EndDeclareModule

Module MapEx
  
  DisableDebugger
  
  ;->> Procedures <<  
  
  Procedure GetMapPointer(*Map)
    CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
      !mov rdx, [p.p_Map]
      !mov rax, [rdx + 72]
    CompilerElse
      !mov edx, [p.p_Map]
      !mov eax, [edx + 48] 
    CompilerEndIf
    ProcedureReturn
  EndProcedure
  
  Procedure NumericMapKey(*Map)
    CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
      !mov rdx, [p.p_Map]
      !mov rdx, [rdx]
      !mov rax, [rdx + 8]
    CompilerElse
      !mov edx, [p.p_Map]
      !mov edx, [edx]
      !mov eax, [edx + 4]
    CompilerEndIf
    ProcedureReturn
  EndProcedure
  
  ;->> Prototype assignments <<
  
  GetMapPointerI = @GetMapPointer()
  GetMapPointerL = @GetMapPointer()
  GetMapPointerQ = @GetMapPointer()
  GetMapPointerS = @GetMapPointer()
  
  NumericMapKeyI = @NumericMapKey()
  NumericMapKeyL = @NumericMapKey()
  NumericMapKeyQ = @NumericMapKey()
  NumericMapKeyS = @NumericMapKey()
  
EndModule



;->> PB imports for numeric integer maps <<

Import ""
  PB_NewNumericMap(ElementSize, *StructureMap, *PtrAddress, HashSize)
  PB_AddNumericMapElement2(Map IntegerMap.i(), Key, ElementCheck = #PB_Map_ElementCheck)
  PB_FindNumericMapElement(Map IntegerMap.i(), Key)
  PB_DeleteNumericMapElement2(Map IntegerMap.i(), Key)
  PB_GetNumericMapElement(Map IntegerMap.i(), Key)
EndImport




; Create a dummy map and free it again
; this way we have a variable that is 
; marked as a map
NewMap NumericMap.i()
*NumericMap = MapEx::GetMapPointerI(NumericMap())
FreeMap(NumericMap())

; Create a numeric map for the same variable
PB_NewNumericMap(SizeOf(Integer), 0, *NumericMap, 512)

; Work with the numeric map

PB_AddNumericMapElement2(NumericMap(), 12)
NumericMap() = 123

PB_AddNumericMapElement2(NumericMap(), 34)
NumericMap() = 345

PushMapPosition(NumericMap())

PB_AddNumericMapElement2(NumericMap(), 56)
NumericMap() = 765

PB_AddNumericMapElement2(NumericMap(), 56, #PB_Map_NoElementCheck)
NumericMap() = 567

PopMapPosition(NumericMap())
Debug NumericMap()

PB_FindNumericMapElement(NumericMap(), 56)
Debug NumericMap()

*Int.Integer = PB_GetNumericMapElement(NumericMap(), 12)
Debug *Int\i

ForEach NumericMap()
  Debug "Key:" + Str(MapEx::NumericMapKeyI(NumericMap())) + "  Value: " + Str(NumericMap())
Next

FreeMap(NumericMap())
Unfortunately the imports would need to be changed for a map containing strings. :(
But is shows the procedures themselves are there.

@Fred, is there a reason this functionality is not exposed ?
It would be very useful to be able to use a numeric value as a key.

Re: Using adresses as a key for AddMapElements ???

Posted: Fri Dec 13, 2019 1:52 pm
by Joris
Thanks for the efforts people (masters).

@Wilbert I get this (PB 5.71 LTS on XP 32, yeah old hé) :
One value 345 and then :
"The debugged executable quit unexpectedly."
wilbert wrote:It would be very useful to be able to use a numeric value as a key.
Indeed.

Re: Using adresses as a key for AddMapElements ???

Posted: Fri Dec 13, 2019 2:14 pm
by wilbert
Joris wrote:@Wilbert I get this (PB 5.71 LTS on XP 32, yeah old hé) :
One value 345 and then :
"The debugged executable quit unexpectedly."
Does it make a difference if you change ImportC to Import ?

An alternative could be to have a faster function to convert a memory pointer to a string.
Or if you don't have to store a lot of values, a tree instead of a map.

Re: Using adresses as a key for AddMapElements ???

Posted: Fri Dec 13, 2019 5:03 pm
by chi
Thanks wilbert! Any chance you could get this to work with custom structures?

Code: Select all

Structure window
  NewList controls.i()
  hWnd.i
  value.i
EndStructure
wilbert wrote:@Fred, is there a reason this functionality is not exposed ?
It would be very useful to be able to use a numeric value as a key.
I'd be pretty interested in that, too

Re: Using adresses as a key for AddMapElements ???

Posted: Fri Dec 13, 2019 10:17 pm
by idle
chi wrote:Thanks wilbert! Any chance you could get this to work with custom structures?

Code: Select all

Structure window
  NewList controls.i()
  hWnd.i
  value.i
EndStructure
wilbert wrote:@Fred, is there a reason this functionality is not exposed ?
It would be very useful to be able to use a numeric value as a key.
I'd be pretty interested in that, too
It can be used with structures, It just needs the structure address filled out, so for strings or any structure with a PB_Object, you just pass in the address

example tested on linux x64 and Windows x86

Code: Select all

Import ""
  PB_NewNumericMap(ElementSize.i,*StructureMap,*Address,HashTabelSize.l);
  PB_FreeMap(*Map);
  PB_ResetMap(*Map);
  PB_ClearMap(*Map);
  PB_MapSize(*Map) ;
  
  PB_PushMapPosition(*map);
  PB_PopMapPosition(*map);
  
  PB_NextMapElement(*Map);
  
  PB_FindNumericMapElement(*Map,Key.i);
  PB_GetNumericMapElement(*Map,Key.i);
  
  PB_AddNumericMapElement(*Map,Key.i);
  PB_AddNumericMapElement2(*Map,Key.i,ElementCheck=#PB_Map_ElementCheck);
  
  PB_DeleteNumericMapElement2(*Map,Key.i);
  PB_DeleteNumericMapElement(*Map);
  
  PB_CopyMap(*Map,*DestinationMap);
  PB_CopyMap2(*Map,*DestinationMap,Clear.l);
  ;
  PB_MapKey(*Map,PreviousPosition.l);
   
EndImport

Macro NewNumericMap(pmap,mapsize,Type)  
  Global structadr 
  EnableASM
  CompilerIf TypeOf(Type) = #PB_Structure   
    CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
      lea rax,[s_#Type] 
      mov [v_structadr],rax
    CompilerElse 
      lea eax,[s_#Type] 
      mov [v_structadr],eax
    CompilerEndIf
  CompilerElse 
    structadr = 0 
  CompilerEndIf  
  DisableASM 
  PB_NewNumericMap(SizeOf(Type),structadr,@pmap,mapsize) 
EndMacro   


Structure mFoo
  List controls.i()
  i.i 
  s.s 
EndStructure   

Global *mp,*el.mFoo

NewNumericMap(*mp,512,mfoo)

*el = PB_AddNumericMapElement2(*mp,123)
AddElement(*el\controls())
*el\controls() = 123
AddElement(*el\controls())
*el\controls() = 345
*el\i = 123
*el\s = "Hello" 
;
*el = PB_AddNumericMapElement2(*mp,345)
AddElement(*el\controls())
*el\controls() = 678
AddElement(*el\controls())
*el\controls() = 911

*el\i = 345
*el\s = "World"
;
*el = PB_FindNumericMapElement(*mp,123)
ForEach *el\controls() 
  Debug *el\controls() 
Next   
Debug *el\i
Debug *el\s 

*el = PB_FindNumericMapElement(*mp,345)
ForEach *el\controls() 
  Debug *el\controls() 
Next   
Debug *el\i
Debug *el\s 

PB_FreeMap(*mp)

Re: Using adresses as a key for AddMapElements ???

Posted: Sat Dec 14, 2019 3:13 am
by chi
This is fantastic! Thank you, idle 8)

Now I can finally get rid of converting the hWnd to a string first...

Re: Using adresses as a key for AddMapElements ???

Posted: Sat Dec 14, 2019 4:05 am
by idle
chi wrote:This is fantastic! Thank you, idle 8)

Now I can finally get rid of converting the hWnd to a string first...
try this, think I've got it sorted so you can use native types but might not work on osx

Code: Select all

;NumericMap hack for structured Numeric keyed Maps 
;windows linux osx 

Import ""
  PB_NewNumericMap(ElementSize.i,*StructureMap,*Address,HashTabelSize.l);
  PB_FreeMap(*Map)                                                      ;
  PB_ResetMap(*Map)                                                     ;
  PB_ClearMap(*Map)                                                     ;
  PB_MapSize(*Map)                                                      ;
  PB_PushMapPosition(*map)                                              ;
  PB_PopMapPosition(*map)                                               ;
  PB_NextMapElement(*Map)                                               ;
  PB_FindNumericMapElement(*Map,Key.i)                                  ;
  PB_AddNumericMapElement(*Map,Key.i)                                   ;
  PB_AddNumericMapElement2(*Map,Key.i,ElementCheck=#PB_Map_ElementCheck);
  PB_DeleteNumericMapElement2(*Map,Key.i)                               ;
  PB_DeleteNumericMapElement(*Map)                                      ;
  PB_CopyMap(*Map,*DestinationMap)                                      ;
  PB_CopyMap2(*Map,*DestinationMap,Clear.l)                             ;
EndImport

Macro NewNumericMap(pmap,HashTableSize,StructureType,IsStructure=0) 
  Global structadr=0
  EnableASM
  CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
    CompilerIf IsStructure 
      lea rax,[s_#StructureType]
      mov [v_structadr],rax
    CompilerEndIf 
  CompilerElse
    CompilerIf IsStructure  
      lea eax,[s_#StructureType]
      mov [v_structadr],eax
    CompilerEndIf 
  CompilerEndIf
  DisableASM
  PB_NewNumericMap(SizeOf(StructureType),structadr,@pmap,HashTableSize)
EndMacro   

Procedure NumericMapkey(*Map) 
  Protected ele,key   
  ele = PeekI(*map)
  key = PeekI(ele+SizeOf(Integer)) 
  ProcedureReturn key 
EndProcedure   

CompilerIf #PB_Compiler_IsMainFile 
  
  ;Note you need to use a structured element pointer even for native types 
  
  Structure foo
    List controls.i()
    i.i 
    s.s 
  EndStructure   
  
  
  Procedure test() 
    Protected mp, *el.foo 
    NewNumericMap(mp,512,foo,#PB_Structure) ;create the map and pass map variable, number of elements, the name of the structure  
    
    *el = PB_AddNumericMapElement(mp,123)   
    
    AddElement(*el\controls())
    *el\controls() = 111
    AddElement(*el\controls())
    *el\controls() = 112
    *el\i = 123
    *el\s = "Hello" 
    ;
    *el = PB_AddNumericMapElement2(mp,345)
    AddElement(*el\controls())
    *el\controls() = 113
    AddElement(*el\controls())
    *el\controls() = 114
    
    *el\i = 345
    *el\s = "World"
    ;
    *el = PB_FindNumericMapElement(mp,123)
    ForEach *el\controls() 
      Debug "list Controls " + *el\controls() 
    Next   
    Debug "integer " + *el\i
    Debug "string "  + *el\s 
    
    *el = PB_FindNumericMapElement(mp,345)
    ForEach *el\controls() 
      Debug "list Controls " + *el\controls() 
    Next   
    Debug "integer " + *el\i
    Debug "string "  + *el\s 
    
    Debug "+++++++++++"
    Debug "walk the map"
    Debug "+++++++++++"
    
    PB_ResetMap(mp) 
    Repeat 
      *el = PB_NextMapElement(mp) 
      If *el  
        Debug "mapkey " + NumericMapkey(mp) 
        ForEach *el\controls() 
          Debug "list Controls " + *el\controls() 
        Next   
        Debug "integer " + *el\i
        Debug "string "  + *el\s 
      EndIf   
    Until *el=0   
    
    Debug "++++++++++++++"
    Debug "clear map" 
    PB_ClearMap(mp);
    PB_ResetMap(mp) 
    Repeat 
      *el = PB_NextMapElement(mp) 
      If *el  
        Debug "mapkey " + NumericMapkey(mp) 
        ForEach *el\controls() 
          Debug "list Controls " + *el\controls() 
        Next   
        Debug "integer " + *el\i
        Debug "string "  + *el\s 
      EndIf   
    Until *el=0   
    Debug "should be nothing" 
    
    PB_FreeMap(mp)
    
    Debug "++++++++++++"
    Debug "test with a native type via pointer"  
    Protected *elf.float   
    NewNumericMap(mp,512,float) ;create the map and pass map variable, number of elements, the name of the structure 
    *elf = PB_AddNumericMapElement(mp,123)
    *elf\f = #PI 
    *elf = PB_AddNumericMapElement(mp,345)
    *elf\f = 2*#PI 
    *elf = PB_FindNumericMapElement(mp,123)
    Debug *elf\f 
    
    *elf = PB_FindNumericMapElement(mp,345)
    If *elf 
      Debug *elf\f 
    EndIf 
    
    Debug NumericMapkey(mp)
    
    PB_FreeMap(mp) 
    
  EndProcedure 
  
  test() 
  
CompilerEndIf 

Re: Using adresses as a key for AddMapElements ???

Posted: Sat Dec 14, 2019 9:35 am
by Joris
wilbert wrote:Does it make a difference if you change ImportC to Import ?
I tried but no better result.

Re: Using adresses as a key for AddMapElements ???

Posted: Thu Dec 19, 2019 9:54 am
by wilbert
Joris wrote:
wilbert wrote:Does it make a difference if you change ImportC to Import ?
I tried but no better result.
Approximately how many items with a numeric key do you need to store ?
Hundreds, thousands ?

Re: Using adresses as a key for AddMapElements ???

Posted: Thu Dec 19, 2019 12:46 pm
by Joris
wilbert wrote:... Approximately how many items with a numeric key do you need to store ?
Hundreds, thousands ?
wilbert it can vary by user use of the program, but I suppose it will mostly be less then 100, but yeah, that's my guess...