Using adresses as a key for AddMapElements ???

Bare metal programming in PureBasic, for experienced users
Joris
Enthusiast
Enthusiast
Posts: 736
Joined: Fri Oct 16, 2009 10:12 am
Location: BE

Using adresses as a key for AddMapElements ???

Post 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.
Yeah I know, but keep in mind ... Leonardo da Vinci was also an autodidact.
User avatar
Fig
Enthusiast
Enthusiast
Posts: 348
Joined: Thu Apr 30, 2009 5:23 pm
Location: Côtes d'Azur, France

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

Post 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.
There are 2 methods to program bugless.
But only the third works fine.

Win10, Pb x64 5.71 LTS
User avatar
idle
Addict
Addict
Posts: 3628
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

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

Post 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 
Joris
Enthusiast
Enthusiast
Posts: 736
Joined: Fri Oct 16, 2009 10:12 am
Location: BE

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

Post 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.
Yeah I know, but keep in mind ... Leonardo da Vinci was also an autodidact.
User avatar
idle
Addict
Addict
Posts: 3628
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

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

Post 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!
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3734
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

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

Post 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.
Last edited by wilbert on Sat Dec 14, 2019 6:44 am, edited 1 time in total.
macOS 10.15 Catalina, Windows 10
Joris
Enthusiast
Enthusiast
Posts: 736
Joined: Fri Oct 16, 2009 10:12 am
Location: BE

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

Post 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.
Yeah I know, but keep in mind ... Leonardo da Vinci was also an autodidact.
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3734
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

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

Post 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.
macOS 10.15 Catalina, Windows 10
User avatar
chi
Addict
Addict
Posts: 825
Joined: Sat May 05, 2007 5:31 pm
Location: Linz, Austria

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

Post 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
Et cetera is my worst enemy
User avatar
idle
Addict
Addict
Posts: 3628
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

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

Post 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)
User avatar
chi
Addict
Addict
Posts: 825
Joined: Sat May 05, 2007 5:31 pm
Location: Linz, Austria

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

Post by chi »

This is fantastic! Thank you, idle 8)

Now I can finally get rid of converting the hWnd to a string first...
Et cetera is my worst enemy
User avatar
idle
Addict
Addict
Posts: 3628
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

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

Post 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 
Joris
Enthusiast
Enthusiast
Posts: 736
Joined: Fri Oct 16, 2009 10:12 am
Location: BE

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

Post by Joris »

wilbert wrote:Does it make a difference if you change ImportC to Import ?
I tried but no better result.
Yeah I know, but keep in mind ... Leonardo da Vinci was also an autodidact.
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3734
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

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

Post 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 ?
macOS 10.15 Catalina, Windows 10
Joris
Enthusiast
Enthusiast
Posts: 736
Joined: Fri Oct 16, 2009 10:12 am
Location: BE

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

Post 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...
Yeah I know, but keep in mind ... Leonardo da Vinci was also an autodidact.
Post Reply