PureLibrary Creator - PB 6.20

Share your advanced PureBasic knowledge/code with the community.
dcr3
Enthusiast
Enthusiast
Posts: 181
Joined: Fri Aug 04, 2017 11:03 pm

Re: Lib - PB 6.20

Post by dcr3 »

pf shadoko wrote: Mon Dec 16, 2024 8:06 pm no, these limitations no longer apply
Have you tested it?
Yes.I have tested.

Tell me why the following doesn't work?

Code: Select all

ProcedureDLL Split(String.s, Array StringArray.s(1), Separator.s = " ")
  
  Protected S.String, *S.Integer = @S
  Protected.i asize, i, p, slen
  asize = CountString(String, Separator)
  slen = Len(Separator)
  ReDim StringArray(asize)
  
  *S\i = @String
  While i < asize
    p = FindString(S\s, Separator)
    StringArray(i) = PeekS(*S\i, p - 1)
    *S\i + (p + slen - 1) << #PB_Compiler_Unicode
    i + 1
  Wend
  StringArray(i) = S\s
  *S\i = 0
  
EndProcedure
User avatar
ChrisR
Addict
Addict
Posts: 1466
Joined: Sun Jan 08, 2017 10:27 pm
Location: France

Re: Lib - PB 6.20

Post by ChrisR »

I've just tested it and it works fine here, what's the name of your source file?

Image
User avatar
pf shadoko
Enthusiast
Enthusiast
Posts: 385
Joined: Thu Jul 09, 2015 9:07 am

Re: Lib - PB 6.20

Post by pf shadoko »

my tool bugs if the program starts with "procedureDLL"
but since you have to put :
DisablePureLibrary LibraryName
...

@morosh
you don't have the right to write in “program file”.
(according to Fred, this will be corrected for the next beta)
User avatar
kenmo
Addict
Addict
Posts: 2032
Joined: Tue Dec 23, 2003 3:54 am

Re: Lib - PB 6.20

Post by kenmo »

Fred wrote: Mon Dec 16, 2024 3:03 pm You can use the /IGNORERESIDENT flag to ignore the load of a resident (instead of deleting it)
Nice tip, I don't think I've ever used this flag before!
dcr3
Enthusiast
Enthusiast
Posts: 181
Joined: Fri Aug 04, 2017 11:03 pm

Re: Lib - PB 6.20

Post by dcr3 »

I created other libs successfully.

I decided to use this procedure on it's own and missed
to use DisablePureLibrary LibraryName. :oops:
pf shadoko wrote: Mon Dec 16, 2024 11:45 pm but since you have to put :
DisablePureLibrary LibraryName
Thanks for pointing that out.

Thanks for a great tool. :D
User avatar
le_magn
Enthusiast
Enthusiast
Posts: 277
Joined: Wed Aug 24, 2005 12:11 pm
Location: Italia

Re: Lib - PB 6.20

Post by le_magn »

I have a possibly stupid question, if I create the library with the 64bit compiler will this library only work to create 64bit code via the 64bit compiler?
Image
User avatar
idle
Always Here
Always Here
Posts: 5835
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: Lib - PB 6.20

Post by idle »

le_magn wrote: Tue Dec 24, 2024 6:56 pm I have a possibly stupid question, if I create the library with the 64bit compiler will this library only work to create 64bit code via the 64bit compiler?
Yes and only with rhe c backend too.
User avatar
le_magn
Enthusiast
Enthusiast
Posts: 277
Joined: Wed Aug 24, 2005 12:11 pm
Location: Italia

Re: Lib - PB 6.20

Post by le_magn »

I need to take a look at the tool's code, to see if I can get it to create the library in the libraries folder of the selected compiler, because having configured several compilers, installed in as many purebasic directories the tool creates the library for me in the purebasic I am currently using while it would serve in the dir of the compiler used

Basically, the tool always uses the same directory as the compiler used to create the tool itself and not the directory of the compiler currently selected in the preferences to create the library....

How to pass to CreateLib tool the current source selected compiler home?
Image
User avatar
idle
Always Here
Always Here
Posts: 5835
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: Lib - PB 6.20

Post by idle »

The libs might work with a 6.12lts but they're really only designed to work with 6.20
User avatar
le_magn
Enthusiast
Enthusiast
Posts: 277
Joined: Wed Aug 24, 2005 12:11 pm
Location: Italia

Re: Lib - PB 6.20

Post by le_magn »

idle wrote: Wed Dec 25, 2024 12:14 am The libs might work with a 6.12lts but they're really only designed to work with 6.20
Yes but if i have compiled the tool with the 6.20 x86 and i use it to compile any lib with configured compiler option 64bit, the tool always use the x86 compiler path to create the lib, i have to manual move the created library in x64 directory, viceversa if the tool is created with 32bit compiler from 64bit purebasic ide it always create the library in 64bit directory and i have to move it in 32bit directory, do you know how to pass the compiler directory currently selected in the options to the tool?
Image
User avatar
mk-soft
Always Here
Always Here
Posts: 6202
Joined: Fri May 12, 2006 6:51 pm
Location: Germany

Re: Lib - PB 6.20

Post by mk-soft »

Hello shadoko

I took a closer look at your tool.

You rebuild the code first.
To do this, it goes through the parameters and creates a new procedureDLL xyz2 for optional parameters. The procedure is then called in this with optional parameters.

This is not optimal as all parameters are processed twice.

But your tool works. :wink:
My Projects ThreadToGUI / OOP-BaseClass / EventDesigner V3
PB v3.30 / v5.75 - OS Mac Mini OSX 10.xx - VM Window Pro / Linux Ubuntu
Downloads on my Webspace / OneDrive
User avatar
pf shadoko
Enthusiast
Enthusiast
Posts: 385
Joined: Thu Jul 09, 2015 9:07 am

Re: Lib - PB 6.20

Post by pf shadoko »

1) libs are system-specific
if you have several PB versions (windows64, windows32, linux, mac), you need to create a lib for each system

2) libs can be used with the c backend and the ASM backend (like all original PB libs)
(on the other hand, you need to create them with the C backend (which is what this tool does, and which is preferable, as it's more optimized).

@mk-soft: I think it's still optimal because of the C optimizations.
do the test
if not, I'll use macros
User avatar
idle
Always Here
Always Here
Posts: 5835
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: Lib - PB 6.20

Post by idle »

There might be some issues using the libs in the asm backend.
Fred
Administrator
Administrator
Posts: 18153
Joined: Fri May 17, 2002 4:39 pm
Location: France
Contact:

Re: Lib - PB 6.20

Post by Fred »

It should work, did you encounter any issue ?
User avatar
idle
Always Here
Always Here
Posts: 5835
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: Lib - PB 6.20

Post by idle »

Yes the linker spat out some symbols not found but I was using it via exporting an interface only which gets around the issue of options in functions. the lib works with c backend but not asm, it's compiled with thread safe

error is
Error: Linker
error: undefined symbol: ___chkstk_ms
>>> referenced by Squintlib.lib(purebasicnothread.obj):(f_squintwalkbinary)
>>> referenced by Squintlib.lib(purebasicnothread.obj):(f_squintwalknode)
>>> referenced by Squintlib.lib(purebasicnothread.obj):(f_squintwalknumeric)
>>> referenced 2 more times
code

Code: Select all


; Macro Comments() 
;   ; SQUINT 3, Sparse Quad Union Indexed Nibble Trie
;   ; Copyright Andrew Ferguson aka Idle (c) 2020 - 2024 
;   ; Version 3.2.2 b2
;   ; PB 5.72-6.02b 32bit/64bit asm and c backends for Windows,Mac OSX,Linux,PI,M1
;   ; Thanks Wilbert for the high low insight and utf8 conversion help.
;   ; Squint is a lock free concurrent compact prefix Trie indexed by nibbles into a sparse array with performance metrics close to a map
;   ; It provides O(K) performance with a memory size ~32 times smaller than a 256 node trie
;   ; Squint is at worst 2 times slower than a Map for set operations, look ups are closer to 1:1 or faster   
;   ; as squint can bail out as soon as a char of a key isn't found unlike a map that has to evaluate the whole key. 
;   ; Squint is lexographicaly sorted so sorting is magnitudes faster than what you could achieve with a map list or unsorted array 
;   ; Squint also supports collections or subtries, which facilitates tasks like in memory DB's  
;   ; The Numeric mode of squint behaves like a map and is closer to 1:1 perfromace with a sized map 
;   ; 
;   ; see https://en.wikipedia.org/wiki/Trie 
;   ;     https://en.wikipedia.org/wiki/Ctrie
;   ;     https://dotat.at/prog/qp/blog-2015-10-04.html
;   ;     https://cr.yp.to/critbit.html 
;   ;
;   ; Squint supports Set, Get, Enum, EnumNode , Walk, WalkNode, Merge, Delete and Prune with a flag in Delete
;   ; String keys can be Unicode, Ascii or UTF8 the type must be specified 
;   ; all string keys get mapped to UTF8 
;   ;
;   ; SquintNumeric supports, SetNumeric GetNumeric DeleteNumeric and WalkNumeric
;   ; it's provided as a direct subtitute for a map, keys can be any size upto #SQUINT_MAX_KEY =1024
;   ; keys are returned as pointers in walk     
;   ; keys can be anything that's serial and optionaly you can hash longer keys which will be sizeof integer 
;   ;
;   ; Note while you can mix string and numeric keys in the same trie it's not recomended unless you only require set and get 
;   ;  
;   ; Eclipse Public License - v 2.0
;   ;
;   ; mzHash64 https://github.com/matteo65/mzHash64 
;   
; EndMacro 

  
  #SQUINT_MAX_KEY = 1024
  
  Structure squint_node Align #PB_Structure_AlignC
    *vertex.edge
    StructureUnion
      squint.q
      value.i 
    EndStructureUnion 
  EndStructure   
  
  Structure edge   Align #PB_Structure_AlignC
    e.squint_node[0]
  EndStructure 
  
  Structure squint Align #PB_Structure_AlignC
    *vt
    size.i
    count.i
    mwrite.i
    menum.i
    *cursor.squint 
    *merge.squint
    *root.squint_node
    sb.a[#SQUINT_MAX_KEY]
  EndStructure
  
  CompilerIf #PB_Compiler_32Bit 
    #Squint_Pmask = $fffffffe
    #Squint_Integer = 4 
  CompilerElse
    #Squint_Pmask = $fffffffffffe
    #Squint_Integer = 8 
  CompilerEndIf
  
  ;External Squint Callback prototype 
  Prototype Squint_CB(*key,*value=0,*userdata=0)
  ;External 
  Prototype Squint_CBFree(*mem) 
  
  DeclareDLL SquintNew()
  Declare SquintFree(*this.Squint,*pfn.Squint_CBFree=0)
  Declare SquintMerge(*this.squint,*target.squint,freesource=0,numeric=0) 
  
  Declare SquintSetNode(*this.squint,*subtrie,*key,value.i,mode=#PB_Unicode)
  Declare SquintGetNode(*this.squint,*subtrie,*key,mode=#PB_Unicode,bval=1)
  Declare SquintDeleteNode(*this.squint,*subtrie,*key,prune=0,mode=#PB_Unicode)
  Declare SquintWalkNode(*this.squint,*subtrie,*pfn.squint_CB,*userdata=0) 
  Declare SquintEnum(*this.squint,*key,*pfn.squint_CB,*userdata=0,mode=#PB_Unicode)
  Declare SquintEnumNode(*this.squint,*subtrie,*key,*pfn.squint_CB,*userdata=0,mode=#PB_Unicode)
  
  Declare SquintSetNumeric(*this.squint,*key,value.i,size=#Squint_Integer,bhash=0)
  Declare SquintGetNumeric(*this.squint,*key,size = #Squint_Integer,bhash=0)
  Declare SquintDeleteNumeric(*this.squint,*key,size = #Squint_Integer,bhash=0)
  Declare SquintWalkNumeric(*this.squint,*pfn.squint_CB,size=#Squint_Integer,*userdata=0)  
  
  Declare SquintSetBinary(*this.squint,*subtrie,*key,value.i,size)
  Declare SquintGetBinary(*this.squint,*subtrie,*key,size)
  Declare SquintDeleteBinary(*this.squint,*subtrie,*key,size,prune=0)
  Declare SquintEnumBinary(*this.squint,*subtrie,*key,size,*pfn.squint_CB,*userdata=0)
  Declare SquintWalkBinary(*this.squint,*subtrie,*pfn.squint_CB,size,*userdata=0) 
  
  Declare SquintSize(*this.squint)
  Declare SquintNumKeys(*this.squint)
    
  
  ;Residents 
  Interface iSquint
    Free(*pfn)
    Merge(*target,freesource=0,numeric=0)  
    Delete(*subtrie,*key,prune=0,mode=#PB_Unicode)
    Set(*subtrie,*key,value.i,mode=#PB_Unicode)
    Get(*subtrie,*key,mode=#PB_Unicode,bval=1)
    Enum(*key,*pfn,*userdata=0,mode=#PB_Unicode)
    EnumNode(*subtrie,*key,*pfn,*userdata=0,mode=#PB_Unicode)
    Walk(*subtrie,*pfn,*userdata=0)
    SetNumeric(*key,value.i,size=#PB_Integer,bhash=0) 
    GetNumeric(*key,size= #PB_Integer,bhash=0) 
    DeleteNumeric(*key,size=#PB_Integer,bhash=0)
    WalkNumeric(*pfn,size=#PB_Integer,*userdata=0)
    SetBinary(*subtrie,*key,value.i,size) 
    GetBinary(*subtrie,*key,size) 
    DeleteBinary(*subtrie,*key,size,prune=0) 
    EnumBinary(*subtrie,*key,size,*pfn,*userdata=0)
    WalkBinary(*subtrie,*pfn,size,*userdata=0) 
    Size()
    NumKeys()
  EndInterface
  ;EndResidents  
  
   
   DataSection: vtSquint:
    Data.i @SquintFree()
    Data.i @SquintMerge() 
    Data.i @SquintDeleteNode() 
    Data.i @SquintSetNode()
    Data.i @SquintGetNode()
    Data.i @SquintEnum()
    Data.i @SquintEnumNode() 
    Data.i @SquintWalkNode()
    Data.i @SquintSetNumeric()
    Data.i @SquintGetNumeric()
    Data.i @SquintDeleteNumeric()
    Data.i @SquintWalkNumeric() 
    Data.i @SquintSetBinary() 
    Data.i @SquintGetBinary() 
    Data.i @SquintDeleteBinary() 
    Data.i @SquintEnumBinary()
    Data.i @SquintWalkBinary()
    Data.i @SquintSize() 
    Data.i @SquintNumKeys()
  EndDataSection   
  
  
  EnableExplicit
  
  ;-macros 
  Macro _SETINDEX(in,index,number)
    in = in & ~(15 << (index << 2)) | (number << (index << 2))
  EndMacro
  
  Macro _GETNODECOUNT()
    CompilerIf #PB_Compiler_32Bit 
      nodecount = MemorySize(*node\vertex) / SizeOf(squint_node)
    CompilerElse
      nodecount = (*node\vertex >> 48)
    CompilerEndIf
  EndMacro
  
  Macro _POKENHL(in,Index,Number)
    *Mem.Ascii = in
    *Mem + Index >> 1
    If Index & 1
      *Mem\a = (*Mem\a & $f0) | (Number & $f)
    Else
      *Mem\a = (*Mem\a & $0f) | (Number << 4)
    EndIf
  EndMacro
  
  CompilerIf #PB_Compiler_Processor = #PB_Processor_x86 
    Macro rax : eax : EndMacro 
  CompilerEndIf   
  
  CompilerIf #PB_Compiler_Thread 
    Macro _LockMutex(mut) 
      LockMutex(mut) 
    EndMacro 
    Macro _UnlockMutex(mut)
      UnlockMutex(mut)
    EndMacro   
  CompilerElse 
    Macro _Lockmutex(mut) 
    EndMacro 
    Macro _UnlockMutex(mut)
    EndMacro   
  CompilerEndIf   
  
  Macro _gLockXCHG(var,var1) 
    CompilerIf #PB_Compiler_Backend = #PB_Backend_C 
      !__atomic_exchange_n(&p_node->f_vertex,p_new,__ATOMIC_SEQ_CST) ; 
    CompilerElse 
      CompilerIf #PB_Compiler_Processor = #PB_Processor_x86
        !mov eax , [p.p_#var1]
        !mov edx , [p.p_#var]
        !xchg dword [edx] , eax
      CompilerElse 
        !mov rax , [p.p_#var1]
        !mov rdx , [p.p_#var] 
        !lock xchg qword [rdx] , rax
      CompilerEndIf 
    CompilerEndIf 
  EndMacro
  ;External  
  Macro _sfence
    CompilerIf #PB_Compiler_Backend = #PB_Backend_Asm  
      !sfence 
    CompilerElse 
      CompilerIf #PB_Compiler_Processor = #PB_Processor_Arm32 Or #PB_Compiler_Processor = #PB_Processor_Arm64
        !__sync_synchronize();
      CompilerElse   
        !__asm__("sfence" ::: "memory");   
      CompilerEndIf   
    CompilerEndIf   
  EndMacro 
  ;External
  Macro _lfence 
    CompilerIf #PB_Compiler_Backend = #PB_Backend_Asm   
       !lfence
    CompilerElse
      CompilerIf #PB_Compiler_Processor = #PB_Processor_Arm32 Or #PB_Compiler_Processor = #PB_Processor_Arm64 
        !__sync_synchronize();
      CompilerElse  
        !__asm__("lfence" ::: "memory"); 
      CompilerEndIf   
    CompilerEndIf    
  EndMacro 
  
  Macro _CONVERTUTF8() 
    
    vchar = PeekU(*key)
    
    If mode = #PB_Unicode  
      CompilerIf #PB_Compiler_Backend = #PB_Backend_C  
        If vchar > $7f
          If vchar > $7ff
            vchar = $8080E0 | (vchar >> 12) | ((vchar << 2) & $3F00) | ((vchar << 16) & $3F0000)
          Else
            vchar = $80C0 | (vchar >> 6) | ((vchar << 8) & $3F00)
          EndIf
        EndIf   
      CompilerElse  
        
        !mov	eax, [p.v_vchar] 
        !cmp eax, 0x80 
        !jb .l2 
        !cmp	eax, 0x0800
        !jae .l1
        !mov edx, eax 
        !sal	edx, 8
        !and	edx, 0x3f00 
        !sar	eax, 6
        !or	edx, eax
        !or  edx, 0x80C0 
        !mov eax,edx
        !jmp	.l2
        !.l1:
        !mov edx, eax 
        !sal	edx, 16
        !and	edx, 0x3f0000
        !mov ecx, eax 
        !sal ecx, 2 
        !and ecx, 0x3f00
        !or  edx,ecx 
        !sar eax, 12 
        !or  edx, eax 
        !or  edx, 0x8080e0 
        !mov eax,edx 
        !.l2:
        !mov	[p.v_vchar], eax  
        
      CompilerEndIf    
      
    EndIf  
    
  EndMacro 
  
  Macro _MODECHECK()
    _CONVERTUTF8()
    If mode <> #PB_Unicode
      If (vchar >> ((count&1)<<4) & $ff = 0)
        Break
      EndIf 
    EndIf
  EndMacro 
  
  Macro _SETNODE()
    
    If *node\vertex
       
      _GETNODECOUNT()
      If (offset <> 15 Or nodecount = 16)
         XCHG_(@*node,(*node\Vertex\e[offset] & #Squint_Pmask))  
      Else  
        
        *node\Vertex = bts(*node\Vertex)
               
        offset = nodecount
        nodecount+1 
        
        *new = AllocateMemory((nodecount)*SizeOf(squint_node)) 
        *old = *node\vertex & #Squint_Pmask
        CopyMemory(*old,*new,(offset)*SizeOf(squint_node)) 
                
        CompilerIf #PB_Compiler_64Bit; 
          *new | ((nodecount) << 48)
        CompilerEndIf  
               
        XCHG_(@*node\vertex,*new) 
        
        _SETINDEX(*node\squint,idx,offset)
                        
        XCHG_(@*node,(*node\Vertex\e[offset] & #Squint_Pmask))
               
        FreeMemory(*old) 
        
        If *this\merge  
          *this\merge\size + SizeOf(squint_node) 
        Else   
          *this\size  +SizeOf(squint_node) 
        EndIf 
                
      EndIf 
      
    Else
            
      *node\vertex = AllocateMemory(SizeOf(squint_Node))
      
      *node\Vertex = bts(*node\Vertex)
      
      CompilerIf #PB_Compiler_64Bit; 
        *node\vertex | (1 << 48)
      CompilerEndIf
      *node\squint = -1
      _SETINDEX(*node\squint,idx,0)
      
      *node\Vertex = BTC(*node\Vertex)
      
      XCHG_(@*node,(*node\Vertex\e[0] & #Squint_Pmask))
      
      If *this\merge  
       *this\merge\size +SizeOf(squint_node) 
      Else   
        *this\size+SizeOf(squint_node) 
      EndIf 
           
    EndIf 
    
  EndMacro
     
  Procedure XCHG_(*ptr.Integer,v1) 
    
    CompilerIf #PB_Compiler_Backend = #PB_Backend_C 
      !__atomic_exchange_n(&p_ptr->f_i,v_v1,__ATOMIC_SEQ_CST); 
    CompilerElse 
      CompilerIf #PB_Compiler_Processor = #PB_Processor_x86
        !mov ecx,[p.p_ptr]
        !mov eax,[p.v_v1]
        !xchg dword [ecx],eax
      CompilerElse 
        !mov rcx, [p.p_ptr]
        !mov rax, [p.v_v1 ] 
        !xchg qword [rcx],rax
      CompilerEndIf
    CompilerEndIf 
    
  EndProcedure 
  
  Procedure CMPXCHG_(*ptr.Integer,eq,chg) 
    
    CompilerIf #PB_Compiler_Backend = #PB_Backend_C 
      !__atomic_compare_exchange_n(&p_ptr->f_i,&v_eq,v_chg,0,__ATOMIC_SEQ_CST,__ATOMIC_RELAXED); 
    CompilerElse 
      
      CompilerIf #PB_Compiler_Processor = #PB_Processor_x86
        !mov eax, dword [p.v_eq]
        !mov ecx, dword [p.v_chg]
        !mov edx, dword [p.p_ptr] 
        !lock cmpxchg dword [edx],ecx 
      CompilerElse 
        !mov rax, qword [p.v_eq]
        !mov rcx, qword [p.v_chg]
        !mov rdx, qword [p.p_ptr] 
        !lock cmpxchg qword [rdx],rcx 
      CompilerEndIf  
      
    CompilerEndIf 
  EndProcedure
  
  Procedure BTS(*node) 
    CompilerIf #PB_Compiler_Backend = #PB_Backend_C 
      !asm("lock bts %1, %0" : "+m" (p_node) : "r" (0)); 
    CompilerElseIf #PB_Compiler_Processor = #PB_Processor_x86
      !lock BTS dword [p.p_node],0 
    CompilerElse
      !lock BTS qword [p.p_node],0 
    CompilerEndIf  
         
    ProcedureReturn *node  
  EndProcedure 

 Procedure BTC(*node) 
   CompilerIf #PB_Compiler_Backend = #PB_Backend_C 
      !asm("lock btc %1, %0" : "+m" (p_node) : "r" (0)); 
    CompilerElseIf #PB_Compiler_Processor = #PB_Processor_x86
      !lock BTC dword [p.p_node],0 
    CompilerElse
      !lock BTC qword [p.p_node],0 
    CompilerEndIf  
    ProcedureReturn *node
 EndProcedure   
  
  ;-General functions 
  
  ;##################################################################################
  ;# Create a new Squint Trie 
  ;#  
  ;# example    
  ;#   global sq.iSquint = SquintNew() use via an interface with isquint           
  ;#   or 
  ;#   global sq = SquintNew() or normal use      
  ;################################################################################## 
  
  ProcedureDLL SquintNew()
    
    Protected *this.squint,a
    *this = AllocateMemory(SizeOf(squint))
    If *this
      *this\vt = ?vtSquint
      *this\root = AllocateMemory(SizeOf(squint_node)*16)
      *this\mwrite = CreateMutex() 
      *this\menum = CreateMutex()
      
      ProcedureReturn *this
    EndIf
  EndProcedure
  
  Procedure ISquintFree(*this.squint,*node.squint_node=0,*pfn.Squint_CBFree=0)
    Protected a,offset,nodecount
    If Not *node
      ProcedureReturn 0
    EndIf
    For a=0 To 15
      offset = (*node\squint >> (a<<2)) & $f
      If *node\vertex
        _GETNODECOUNT()
        If (offset <> 15 Or nodecount = 16)
          *this\size - SizeOf(squint_node)
          ISquintFree(*this,*node\Vertex\e[offset] & #Squint_Pmask)
        EndIf
      EndIf
    Next
    _LockMutex(*this\mwrite)
    If *node\vertex
      _GETNODECOUNT()
      If *pfn 
        If *node <> *this\root 
          *pfn(*node\value)
        EndIf   
      EndIf 
      FreeMemory(*node\Vertex & #Squint_Pmask) 
      *node\vertex=0
    EndIf
    _UnlockMutex(*this\mwrite) 
    ProcedureReturn *node
  EndProcedure
  
  ;##################################################################################
  ;# Free Squint Trie 
  ;# If you need to free your own pointers, Walk the trie 1st to free them  
  ;# then call sq\free() or SquintFree(sq)   
  ;################################################################################## 
  
  Procedure SquintFree(*this.squint,*pfn.Squint_CBFree=0)  
    
    Protected a,offset,*node.squint_node,nodecount
    
    *node = *this\root
    For a=0 To 15
      offset = (*node\squint >> (a<<2)) & $f
      If *node\vertex
        _GETNODECOUNT()
        If (offset <> 15 Or nodecount = 16)
          ISquintFree(*this,*node,*pfn)
        EndIf
      EndIf
    Next
    FreeMutex(*this\mwrite)
    FreeMutex(*this\menum) 
    FreeMemory(*this\root)
    nodecount = *this\size 
    FreeMemory(*this) 
    
    ProcedureReturn  nodecount  
  EndProcedure
  
  Procedure ISquintMerge(*this.squint,*node.squint_Node,*target.squint,depth,*outkey,numeric)
    Protected a.i,offset,nodecount,*mem.Ascii,key.s,*tnode 
    
    If Not *node
      ProcedureReturn 0
    EndIf
    
    For a =0 To 15
      
      offset = (*node\squint >> (a<<2)) & $f
      If (*node\vertex And *node\squint)
        _GETNODECOUNT()
        If (offset <> 15 Or nodecount = 16)
          _POKENHL(*outkey,depth,a)
          XCHG_(@*tnode,(*node\Vertex\e[offset] & #Squint_Pmask))
          If ISquintMerge(*this,*tnode,*target,depth+1,*outkey,numeric) = 0 
            Break 
          EndIf  
        EndIf
        
      EndIf
    Next
    
    If *node\vertex=0
      
      PokeA(*outkey+((depth>>1)),0)
      If numeric = 0 
        If *node\value
          SquintSetNode(*target,0,*outkey,*node\value,#PB_UTF8)
        EndIf  
      Else 
        If *node\value
          SquintSetNumeric(*target,*outkey,*node\value)
        EndIf   
      EndIf 
    EndIf
    ProcedureReturn *node
    
  EndProcedure
  
  ;#################################################################################
  ;#    merge tries    
  ;#    *this.squint instance from SquintNew() 
  ;#    *target squint to merge into  
  ;#    freesource frees the souce 
  ;##################################################################################
  
  Procedure SquintMerge(*this.squint,*target.squint,freesource=0,numeric=0)   
    
    Protected outkey.s{#SQUINT_MAX_KEY} 
    
    *target\merge=0 
    
    ISquintMerge(*this,*this\root,*target,0,@outkey,numeric)
    
    If freesource
      
      SquintFree(*this) 
      
    EndIf   
    
  EndProcedure   
  
  ;-string functions 
  
  ;#################################################################################
  ;#    Set a node from the root or from a previously set node   
  ;#    *this.squint instance from SquintNew() 
  ;#    *subtrie 0 Or the addess of a previously stored node retuned from this function 
  ;#   
  ;#    *key   address of a null terminated string can be unicode ascii or UTF8
  ;#    value.i  non zero value or address of something  
  ;#    mode.i  Desired key format #PB_Uniocde, #PB_Ascii, #PB_UTF8  
  ;#    returns *subtrie the node       
  ;# example     
  ;#    *cars = SquintSetNode(sq,0,@"cars:",100)          the key = "cars:"        
  ;#     *toyota = squintSetNode(sq,*cars,@"Toyota:",200) the key = "cars:toyota" 
  ;#     squintSetNode(sq,*toyota,@"Corolla",201)         the key = "cars:toyota:corolla" 
  ;#     squintSetNode(sq,*toyota,@"Cameray",202)         the key = "cars:toyota:Cameray"
  ;# via interface  
  ;#     sq\set(*toyota,@"Cameray",202)                   the key = "cars:toyota:Cameray"   
  ;################################################################################## 
  
  Procedure SquintSetNode(*this.squint,*subtrie,*key,value.i,mode=#PB_Unicode)
    
    Protected *node.squint_node,idx,offset,nodecount,vchar.l,vret.l,count,*out
    Protected *new.squint_node,*old.squint_node,*adr 
    Protected bmerge      
      
    
    _LockMutex(*this\mwrite)
    
    XCHG_(@bmerge,*this\merge) 
    If bmerge 
      XCHG_(@*node,(*this\merge\root & #Squint_Pmask))
    Else 
      If *subtrie = 0
        XCHG_(@*node,(*this\root & #Squint_Pmask))
      Else 
        *node = *subtrie & #Squint_Pmask
      EndIf
    EndIf 
      
    _CONVERTUTF8()
    
    While vchar
      
      idx = (vchar >> 4) & $f
      offset = (*node\squint >> (idx<<2)) & $f
      _SETNODE()     
      
      idx = vchar & $0f
      offset = (*node\squint >> (idx<<2)) & $f
      _SETNODE()    
      
      vchar >> 8
      count+1
      If vchar = 0
        *key+2
        _MODECHECK()
      EndIf
      Delay(0)
    Wend
    
    
    idx=0
    *out = *node 
    offset = *node\squint & $f
    
    _SETNODE()
   
    If bmerge 
      *this\merge\count+1 
    Else 
      *this\count +1
    EndIf 
    
    If value 
      *node\value = value
    EndIf 
    
    _UnlockMutex(*this\mwrite)
    
    ProcedureReturn *out
    
  EndProcedure
    
  
  ;##################################################################################
  ;#    Get a node from the root or from a previously stored node aka subtrie    
  ;#    *this.squint instance from SquintNew() 
  ;#    *subtrie 0 Or the addess of a previously stored node retuned from this function 
  ;#   *key   address of a null terminated string can be unicode ascii or UTF8
  ;#    mode.i  Desired key format #PB_Uniocde, #PB_Ascii, #PB_UTF8  
  ;#    
  ;#  returns the value or subnode        
  ;#  example    
  ;#     x = squintGetNode(sq,0,@"cars:toyota:")   subtrie = root, the key = "cars:toyota"  
  ;#     x = squintGetNode(sq,*toyota,@"Corolla")  subtrie = *toyota the key evaluates to = "cars:toyota:corolla"   
  ;#     or via interface 
  ;#     x = sq\get(0,@"cars:toyota:")  subtrie = root, the key = "cars:toyota"    
  ;################################################################################## 
  
  Procedure SquintGetNode(*this.squint,*subtrie,*key,mode=#PB_Unicode,bval=1)
    
    Protected *node.squint_Node,idx,offset,nodecount,vchar.l,vret.l,count,*out
    
    If *subtrie = 0
      XCHG_(@*node,(*this\root & #Squint_Pmask))
    Else 
      XCHG_(@*node,(*subtrie & #Squint_Pmask))
    EndIf 
    _CONVERTUTF8()
    
    If *node\vertex
      
      While vchar
        
        l1:
        If Not (*node & 1) 
          
          offset = (*node\squint >> ((vchar & $f0) >> 2 )) & $f
          _GETNODECOUNT()
          If offset < nodecount
            XCHG_(@*node,(*node\Vertex\e[offset] & #Squint_Pmask))
          Else
            ProcedureReturn 0
          EndIf
        Else 
          Delay(0)
          Goto l1  
        EndIf  
        
        l2:
        If Not (*node & 1) 
          
          offset = (*node\squint >> ((vchar & $0f) << 2)) & $f
          _GETNODECOUNT()
          If offset < nodecount
            XCHG_(@*node,(*node\Vertex\e[offset] & #Squint_Pmask))
          Else
            ProcedureReturn 0
          EndIf
        Else 
          Delay(0)
          Goto l2  
        EndIf 
        
        vchar >> 8
        count+1
        If vchar = 0
          *key+2
          _MODECHECK()
        EndIf
        
      Wend
      
      *out = *node 
      offset = *node\squint & $f
      _GETNODECOUNT()
      
      If offset <= nodecount
        XCHG_(@*node,(*node\Vertex\e[offset] & #Squint_Pmask)) 
        If bval 
          ProcedureReturn *node\value
        Else 
          ProcedureReturn *out  
        EndIf   
      Else
        ProcedureReturn 0
      EndIf
      
    EndIf 
    
  EndProcedure 
  
  Procedure SquintSetCursor(*this.squint,*key,*cursor) 
    
    Protected *node 
    *node = SquintGetNode(*this,0,*key,#PB_Unicode,0) 
    If *node 
      SquintSetNumeric(*this\cursor,*node,*cursor) 
    EndIf  
    *cursor = *node 
    
  EndProcedure  
   
  
  ;##################################################################################
  ;#    Resets a keys value to 0 or deletes the childen of the node freeing up memory     
  ;#    *this.squint instance from SquintNew() 
  ;#    *subtrie 0 Or the addess of a previously stored node retuned from this function 
  ;#    *key     address of a null terminated string can be unicode ascii or UTF8
  ;#    mode.i   Desired key format 
  ;#    returns  the value or 0
  ;#    example  
  ;#    x = SquintDeleteNode(sq,*cars,@"Toyota:",1)  subnode = *cars, the key evals to "cars:toyota" prune =1 So it deletes the child nodes corrola And camery  
  ;#    x = SquintDeleteNode(sq,0,@"cars:toyota:corolla") subtrie = root, the full key = "cars:toyota:corolla" prune=0 so it set the value to 0  
  ;#    via inteface  
  ;#    sq\delete(0,@"cars:toyota:corolla")     
  ;################################################################################## 
  
  Procedure SquintDeleteNode(*this.squint,*subtrie,*key.Unicode,prune=0,mode=#PB_Unicode)
    
    Protected *node.squint_node,idx,*mem.Character,offset,nodecount,vchar.l,vret.l,count,*out
    If *subtrie = 0
      *node = *this\root & #Squint_Pmask
    Else
      *node = *subtrie  & #Squint_Pmask 
    EndIf 
    _CONVERTUTF8()
    While vchar
      offset = (*node\squint >> ((vchar & $f0) >> 2 )) & $f
      If *node\vertex
        _GETNODECOUNT()
        If (offset <> 15 Or nodecount = 16)
          *node = *node\Vertex\e[offset] & #Squint_Pmask
        EndIf
      Else
        ProcedureReturn 0
      EndIf
      If *node
        offset = (*node\squint >> ((vchar & $0f) << 2)) & $f
        If *node\vertex
          _GETNODECOUNT()
          If (offset <> 15 Or nodecount = 16)
            *node = *node\Vertex\e[offset] & #Squint_Pmask
          EndIf
        Else
          ProcedureReturn 0
        EndIf
      EndIf
      vchar >> 8
      If vchar = 0
        *key+2
        _MODECHECK()
      EndIf
    Wend
    If prune
      ISquintFree(*this,*node)
      If (*node\vertex & #Squint_Pmask) = 0
        *node\squint = 0
      EndIf
    Else
      offset = *node\squint & $f
      _GETNODECOUNT()
      If offset <= nodecount
        *node = (*node\Vertex\e[offset] & #Squint_Pmask)
        If (*node\vertex & #Squint_Pmask) = 0
          *node\squint = 0
        EndIf
      Else
        ProcedureReturn 0
      EndIf
    EndIf
  EndProcedure
  
  Procedure IEnum(*this.squint,*node.squint_Node,depth,*pfn.squint_CB,*outkey,*userdata=0)
    Protected a.i,offset,nodecount,*mem.Ascii
    
    If Not *node
      ProcedureReturn 0
    EndIf
    
    For a =0 To 15
      
      offset = (*node\squint >> (a<<2)) & $f
      If (*node\vertex And *node\squint)
        _GETNODECOUNT()
        If (offset <> 15 Or nodecount = 16)
          _POKENHL(*outkey,depth,a)
          If IEnum(*this,(*node\Vertex\e[offset] & #Squint_Pmask),depth+1,*pfn,*outkey,*userdata) = 0 
            Break 
          EndIf  
        EndIf
        
      EndIf
    Next
    
    If *node\vertex=0
      If *pfn
        PokeA(*outkey+((depth>>1)),0)
        If *pfn(*outkey,*node\value,*userdata) = 0 
          ProcedureReturn 0
        EndIf   
      EndIf
    EndIf
    ProcedureReturn *node
    
  EndProcedure
  
  ;##################################################################################
  ;#  Enumerates the Trie from a given key   
  ;#    *this.squint instance from SquintNew() 
  ;#    *subtrie 0 Or the addess of a previously stored node       
  ;#    *key address of a null terminated string can be unicode ascii Or UTF8
  ;#     mode.i  Desired key format #PB_Uniocde, #PB_Ascii, #PB_UTF8  
  ;#    *pfn.squint_CB address of callback function as Squint_CB(*key,*value=0,*userdata=0) 
  ;#        where *key is pointer to the key *value is pointer to the *value, *userDate      
  ;# example    
  ;#     squintEnum(sq,*subtrie,@"cars:toyota:",@MyCallback())       
  ;#  or via interface 
  ;#     sq\Enum@"cars:toyota:",@MyCallback())   
  ;################################################################################## 
  
  Procedure SquintEnumNode(*this.squint,*subtrie,*key,*pfn.squint_CB,*userdata=0,mode=#PB_Unicode)  
    
    Protected *node.squint_Node,idx,*mem.Ascii,offset,nodecount,depth,vchar.l,vret.l,count,*out
    Protected *old.squint,*new.squint,bnmerge
    Protected outkey.s{1024} 
    
    _LockMutex(*this\menum)
    
    *new = SquintNew()     
   
    XCHG_(@*this\merge,*new) 
    
    If *subtrie = 0
       XCHG_(@*node,(*this\root & #Squint_Pmask))
    Else 
       XCHG_(@*node ,(*subtrie & #Squint_Pmask))
    EndIf 
    _CONVERTUTF8()
    
    If *node\vertex
      
      While vchar
        
        l1:
        If Not (*node & 1)
          
          offset = (*node\squint >> ((vchar & $f0) >> 2 )) & $f
          _GETNODECOUNT()
          If offset < nodecount
            XCHG_(@*node,(*node\Vertex\e[offset] & #Squint_Pmask))
          Else
            bnmerge = 1
            Break 
          EndIf
        Else 
          Delay(0)
          Goto l1  
        EndIf  
        
        l2:
        If Not (*node & 1)
         
          offset = (*node\squint >> ((vchar & $0f) << 2)) & $f
          _GETNODECOUNT()
          If offset < nodecount
            XCHG_(@*node,(*node\Vertex\e[offset] & #Squint_Pmask))
          Else
            bnmerge = 1 
            Break 
          EndIf
        Else 
          Delay(0)
          Goto l2
        EndIf 
        
        vchar >> 8
        count+1
        If vchar = 0
          *key+2
          _MODECHECK()
        EndIf
        
      Wend
      
      If bnmerge = 0 
        
        l3: 
        If Not (*node & 1)
          IEnum(*this,*node,depth,*pfn,@outkey,*userdata)
        Else 
          Delay(0)
          Goto l3 
        EndIf   
        
        _LockMutex(*this\mwrite)  
        
        *old = *this\merge
        XCHG_(@*this\merge,0)               
        SquintMerge(*old,*this)
        
        _UnlockMutex(*this\mwrite)
        
        SquintFree(*old)
        
      Else 
        
        _LockMutex(*this\mwrite)  
        *old = *this\merge
        XCHG_(@*this\merge,0)    
        
        _UnlockMutex(*this\mwrite)
        SquintFree(*old)
        
      EndIf 
      
    EndIf 
    
    _UnlockMutex(*this\menum) 
    
    
  EndProcedure
  
  ;##################################################################################
  ;#  Enumerates the Trie from a given key   
  ;#    *this.squint instance from SquintNew() 
  ;#    *key   address of a null terminated string can be unicode ascii or UTF8
  ;#     mode.i  Desired key format #PB_Uniocde, #PB_Ascii, #PB_UTF8  
  ;#    *pfn.squint_CB address of callback function as Squint_CB(*key,*value=0,*userdata=0) 
  ;#        where *key is pointer to the key *value is pointer to the *value, *userDate      
  ;# example    
  ;#     squintEnum(sq,@"cars:toyota:",@MyCallback())       
  ;#  or via interface 
  ;#     sq\Enum@"cars:toyota:",@MyCallback())   
  ;################################################################################## 
  
  Procedure SquintEnum(*this.squint,*key,*pfn.squint_CB,*userdata=0,mode=#PB_Unicode)
    
    SquintEnumNode(*this,0,*key,*pfn,*userdata,mode) 
    
  EndProcedure   
  
  ;##################################################################################
  ;# Walks the entire trie    
  ;#    *this.squint instance from SquintNew() 
  ;#    *pfn.squint_CB address of callback function as Squint_CB(*key,*value=0,*userdata=0) 
  ;#       where *key is pointer to the key *value is pointer to the *value, *userDate      
  ;# example    
  ;#     squintWalk(sq,@MyCallback())       
  ;#  or via interface 
  ;#     sq\Walk(@MyCallback())   
  ;################################################################################## 
  
  Procedure SquintWalk(*this.squint,*pfn.squint_CB,*userdata=0) 
    
    Protected *node, *old.squint,*new.squint, outkey.s{#SQUINT_MAX_KEY} 
    
    _LockMutex(*this\menum)
    
    *new = SquintNew()     
    XCHG_(@*this\merge,*new) 
    
    *node = *this\root & #Squint_Pmask
    
    IEnum(*this,*node,0,*pfn,@outkey,*userdata)
    
    _LockMutex(*this\mwrite)  
    
    *old = *this\merge
    XCHG_(@*this\merge,0)               
    SquintMerge(*old,*this) 
    
    _UnlockMutex(*this\mwrite)
    
    SquintFree(*old)
    
    _UnlockMutex(*this\menum) 
    
  EndProcedure
  
  ;##################################################################################
  ;# Walks from a subtrie    
  ;#    *this.squint instance from SquintNew() 
  ;#    *subtrie 0 Or the addess of a previously stored node  
  ;#    *pfn.squint_CB address of callback function As Squint_CB(*key,*value=0,*userdata=0) 
  ;#          
  ;# example    
  ;#     squintWalkNode(sq,*cars,@MyCallback())       
  ;#  or via interface 
  ;#     sq\Walk(*cars,@MyCallback())   
  ;################################################################################## 
  
  Procedure SquintWalkNode(*this.squint,*subtrie,*pfn.squint_CB,*userdata=0)   
    
    Protected *node, *old.squint,*new.squint, outkey.s{#SQUINT_MAX_KEY}    
    
    _LockMutex(*this\menum)
    
    *new = SquintNew()     
    XCHG_(@*this\merge,*new) 
    
    If *subtrie = 0
      *node = *this\root & #Squint_Pmask 
    Else
      *node = *subtrie  & #Squint_Pmask 
    EndIf 
    
    IEnum(*this,*node,0,*pfn,@outkey,*userdata)
    
    _LockMutex(*this\mwrite)  
    
    *old = *this\merge
    XCHG_(@*this\merge,0)               
    SquintMerge(*old,*this) 
    
    _UnlockMutex(*this\mwrite)
    
    SquintFree(*old)
    
    _UnlockMutex(*this\menum) 
    
  EndProcedure
  
  ;-Binaryfunctions operate the same as the string functions with no utf8 conversion   
  
  ;#################################################################################
  ;#    Set a Binary key  
  ;#    a Binary key is an address to memory and it's size in bytes 
  ;#    
  ;#    *this.squint instance from SquintNew() 
  ;#    *key   address of a variable or memory pointer 
  ;#    value.i non zero value or address of something  
  ;#    size.i required size in bytes    
  ;#  example     
  ;#     pt.point  
  ;#     pt\x = 100 
  ;#     pt\y = 200   
  ;#     SquintSetBinary(sq,@pt,1,SizeOf(point))       
  ;#  via interface  
  ;#     sq\setBinary(@pt,123435,SizeOf(point)))    
  ;################################################################################## 
  
  
  Procedure SquintSetBinary(*this.squint,*subtrie,*key,value.i,size)
    
    Protected *node.squint_node,idx,offset,nodecount,vchar.i,vret.i,count
    Protected bmerge,*old,*new.squint_node,sqindex,*akey.Ascii 
     
    
    _LockMutex(*this\mwrite)
    
    XCHG_(@bmerge,*this\merge) 
    If bmerge 
      *node = *this\merge\root & #Squint_Pmask
    Else 
      If *subtrie = 0
        *node = *this\root & #Squint_Pmask
      Else 
        *node = *subtrie & #Squint_Pmask
      EndIf
    EndIf 
    
    *akey = *key 
    
    While count <= size  
      idx = (*akey\a >> 4) & $f
      offset = (*node\squint >> (idx<<2)) & $f
      _SetNODE()
      idx = (*akey\a & $f)
      offset = (*node\squint >> (idx<<2)) & $f
      _SetNODE()
      *akey+1 
      count+1
    Wend
    
    If bmerge 
      *this\merge\count+1 
    Else 
      *this\count +1
    EndIf 
    
    If value 
      *node\value = value
    EndIf 
    
    _UnlockMutex(*this\mwrite)
    
    ProcedureReturn *node 
    
  EndProcedure
  
  ;##################################################################################
  ;#    Get a Binary node     
  ;#    *key   address of a variable Or memory pointer 
  ;#    size   number of bytes used for the key      
  ;#    #returns the value         
  ;#  example  
  ;#     pt.point  
  ;#     pt\x = 100 
  ;#     pt\y = 200   
  ;#     x = squintGetBinary(sq,@pt,sizeof(point))   
  ;#     or via interface 
  ;#     x = sq\getBinary(@pt,sizeof(point))        
  ;################################################################################## 
  
  Procedure SquintGetBinary(*this.squint,*subtrie,*key,size)
    
    Protected *node.squint_Node,idx,offset,nodecount,vchar.i,vret.i,count,*akey.Ascii,st  
    
    If *subtrie = 0
      *node = *this\root & #Squint_Pmask
    Else
      *node = *subtrie  & #Squint_Pmask 
    EndIf 
    
    *akey=*key 
    
    While count <= size  
      
      l1:
      If Not (*node & 1) 
        offset = (*node\squint >> ((*akey\a & $f0) >> 2 )) & $f
        _GETNODECOUNT()
        If offset < nodecount
          *node = (*node\Vertex\e[offset] & #Squint_Pmask)
        Else
          ProcedureReturn 0
        EndIf
      Else 
        Goto l1
      EndIf  
      
      l2:
      If Not (*node & 1) 
       offset = (*node\squint >> ((*akey\a & $0f) << 2)) & $f
        _GETNODECOUNT()
        If offset < nodecount
          *node = (*node\Vertex\e[offset] & #Squint_Pmask)
        Else
          ProcedureReturn 0
        EndIf
      Else 
        Goto l2
      EndIf  
      *akey+1
      count+1
    Wend
    
    ProcedureReturn *node\value
  EndProcedure
  
  ;##################################################################################
  ;#  Delete Binary resets the keys value to 0      
  ;#    *this.squint instance from SquintNew() 
  ;#    *key   address of a variable or memory pointer 
  ;#    size   number of bytes used to store the key    
  ;#  example  
  ;#     pt.point  
  ;#     pt\x = 100 
  ;#     pt\y = 200   
  ;#     x = SquintDeleteBinary(sq,@pt,SizeOf(point))   
  ;#     or via interface 
  ;#     x = sq\DeleteBinary(@pt,SizeOf(point))      
  ;################################################################################## 
  
  Procedure SquintDeleteBinary(*this.squint,*subtrie,*key,size,prune=0)    
    
    Protected *node.squint_node,idx,*mem.Ascii,*akey.Ascii,offset,nodecount,vchar.l,vret.l,count,*out
    If *subtrie = 0
      *node = *this\root & #Squint_Pmask
    Else
      *node = *subtrie  & #Squint_Pmask 
    EndIf 
    *akey=*key 
    
    While count <= size  
      offset = (*node\squint >> ((*akey\a & $f0) >> 2 )) & $f
      If *node\vertex
        _GETNODECOUNT()
        If (offset <> 15 Or nodecount = 16)
          *node = *node\Vertex\e[offset] & #Squint_Pmask
        EndIf
      Else
        ProcedureReturn 0
      EndIf
      If *node
        offset = (*node\squint >> ((*akey\a & $0f) << 2)) & $f
        If *node\vertex
          _GETNODECOUNT()
          If (offset <> 15 Or nodecount = 16)
            *node = *node\Vertex\e[offset] & #Squint_Pmask
          EndIf
        Else
          ProcedureReturn 0
        EndIf
      EndIf
      *akey+1
      count+1
    Wend
    
    If prune
      ISquintFree(*this,*node)
      If (*node\vertex & #Squint_Pmask) = 0
        *node\squint = 0
      EndIf
    Else
      offset = *node\squint & $f
      _GETNODECOUNT()
      If offset <= nodecount
        *node = (*node\Vertex\e[offset] & #Squint_Pmask)
        If (*node\vertex & #Squint_Pmask) = 0
          *node\squint = 0
        EndIf
      Else
        ProcedureReturn 0
      EndIf
    EndIf
  EndProcedure
  
  ;##################################################################################
  ;#  Enumerates the Trie from a given key   
  ;#    *this.squint instance from SquintNew() 
  ;#    *subtrie 0 Or the addess of a previously stored node       
  ;#    *key address 
  ;#     size the size of the key 
  ;#    *pfn.squint_CB address of callback function as Squint_CB(*key,*value=0,*userdata=0) 
  ;#        where *key is pointer to the key *value is pointer to the *value, *userDate      
  ;# example    
  ;#     pt.point     
  ;#     pt\x = 100 
  ;#     we want to search for all points where x = 100, size of the search key is 4 bytes    
  ;#     squintEnumBinary(sq,*subtrie,@pt,4,@MyCallback())       
  ;#  or via interface 
  ;#     sq\Enum@"cars:toyota:",@MyCallback())   
  ;################################################################################## 
  
  Procedure SquintEnumBinary(*this.squint,*subtrie,*key,size,*pfn.squint_CB,*userdata=0)       
    
    Protected *node.squint_Node,idx,*mem.Ascii,*akey.Ascii,offset,nodecount,depth,vchar.l,vret.l,count
    Protected bnmerge,*old.squint,*new.squint,outkey.s{1024} 
    
    _LockMutex(*this\menum)
    
    *new = SquintNew()     
    XCHG_(@*this\merge,*new) 
    
    If *subtrie = 0
      *node = *this\root
    Else
      *node = *subtrie  & #Squint_Pmask 
    EndIf 
    
    *akey = *key 
    
    While count <= size  
      
      offset = (*node\squint >> ((*akey\a & $f0) >> 2 )) & $f
      _GETNODECOUNT()
      If offset < nodecount
        *mem = @outkey+(depth>>1) 
        *mem\a = (*mem\a & $0f) | (((*akey\a >> 4) & $f)<<4)
        depth+1
        *node = (*node\Vertex\e[offset] & #Squint_Pmask)
      Else
        bnmerge=1
        Break 
      EndIf
      
      offset = (*node\squint >> ((*akey\a & $0f) << 2)) & $f
      _GETNODECOUNT()
      If offset < nodecount
        *mem = @outkey+(depth>>1) 
        *Mem\a = ((*Mem\a & $f0) | (*akey\a & $f))
        depth+1
        *node = (*node\Vertex\e[offset] & #Squint_Pmask)
      Else
        bnmerge=1  
        Break
      EndIf
      
      *akey+1
      count+1
      
    Wend
    
    If bnmerge = 0 
      
      IEnum(*this,*node,depth,*pfn,@outkey,*userdata)
      
      _LockMutex(*this\mwrite)  
      
      *old = *this\merge
      XCHG_(@*this\merge,0)               
      SquintMerge(*old,*this) 
      
      _UnlockMutex(*this\mwrite)
      
      SquintFree(*new)
      
    Else 
      
      *old = *this\merge
      XCHG_(@*this\merge,0)    
      SquintFree(*old)
      
    EndIf 
    
    _UnlockMutex(*this\menum) 
    
  EndProcedure
  
  Procedure SquintWalkBinary(*this.squint,*subtrie,*pfn.squint_CB,size,*userdata=0)    
    
    Protected *node, *old.squint,*new.squint, outkey.s{#SQUINT_MAX_KEY}    
    
    _LockMutex(*this\menum)
    
    *new = SquintNew()     
    XCHG_(@*this\merge,*new) 
    
    If *subtrie = 0
      *node = *this\root
    Else
      *node = *subtrie  & #Squint_Pmask 
    EndIf 
    
    IEnum(*this,*node,0,*pfn,@outkey,*userdata)
    
    _LockMutex(*this\mwrite)  
    
    *old = *this\merge
    XCHG_(@*this\merge,0)               
    SquintMerge(*old,*this) 
    
    _UnlockMutex(*this\mwrite)
    
    SquintFree(*old)
    
    _UnlockMutex(*this\menum) 
    
  EndProcedure   
  
  Procedure SquintSize(*this.squint) 
    ProcedureReturn *this\size 
  EndProcedure   
  
  Procedure SquintNumKeys(*this.squint)
    ProcedureReturn *this\count 
  EndProcedure   
  
  ;-Numeric functions operate the same as a map, keys can be anything that's serial  
  
  ;#################################################################################
  ;#    Set a numeric key  
  ;#    note you can use both numeric or string keys in the same trie  
  ;#    a numeric key is an address to a variable and the required size 4 or 8 bytes on x64 
  ;#    optionally you can use a hash use a hash when you don't want large keys 
  ;#
  ;#    *this.squint instance from SquintNew() 
  ;#    *key   address of a variable or memory pointer 
  ;#    value.i non zero value or address of something  
  ;#    size.i required size in bytes    
  ;#  example     
  ;#     ikey.l = 12345 
  ;#     SquintSetNumeric(sq,@ikey,1234567,4)  the key size is 4 bytes   
  ;#     pt.point  
  ;#     pt\x = 100 
  ;#     pt\y = 200   
  ;#     SquintSetNumeric(sq,@pt,1,SizeOf(point),#true) ;hashes the key         
  ;#  via interface  
  ;#     sq\setNumeric(@ikey,123435,4)    
  ;################################################################################## 
  
  Procedure SquintSetNumeric(*this.squint,*key,value.i,size=#Squint_Integer,bhash=0)
    
    Protected *node.squint_node,idx,offset,nodecount,vchar.i,vret.i,count,hash.q 
    Protected bmerge,*old,*new.squint_node,sqindex,*akey.Ascii 
      
    
    _LockMutex(*this\mwrite)
    
    XCHG_(@bmerge,*this\merge) 
    If bmerge 
      XCHG_(@*node,(*this\merge\root & #Squint_Pmask))
    Else 
      XCHG_(@*node,(*this\root & #Squint_Pmask))
    EndIf  
        
    If bhash 
      *akey=*key 
      hash = $D45E69F901E72147 ! bhash;
      Repeat 
        hash = $3631754B22FF2D5C * (count + *akey\a) ! (hash << 2) ! (hash >> 2);
        *akey + 1
        count+1 
      Until count >= size 
      count = 0 
      size = 8
      *akey = @hash+(#Squint_Integer-1)
    Else 
      *akey = *key+(size-1)
    EndIf 
    
    While count <= size  
      
      idx = (*akey\a >> 4) & $f
      offset = (*node\squint >> (idx<<2)) & $f
      _SetNODE()
      
      idx = (*akey\a & $f)
      offset = (*node\squint >> (idx<<2)) & $f
      _SetNODE()
      *akey-1 
      count+1
      Delay(0)
    Wend
    
    If bmerge 
      *this\merge\count+1 
    Else 
      *this\count +1
    EndIf 
    
    *node\value = value
            
    _UnlockMutex(*this\mwrite)
    
    ProcedureReturn *node
    
  EndProcedure
  
  ;##################################################################################
  ;#    Get a numeric node     
  ;#    *this.squint instance from SquintNew() 
  ;#    *key   address of a variable or memory pointer 
  ;#    size   number of bytes used to store the key 4 or 8 x64 or an arbitatry size if using the hash     
  ;#    bhash  set to #true if your hashing the key  
  ;#    #returns the value         
  ;#  example  
  ;#    key.l = 12345  
  ;#    x = squintGetNumeric(sq,@ikey,4)   
  ;#    or via interface 
  ;#    x = sq\get(@ikey,4)      
  ;################################################################################## 
  
  Procedure SquintGetNumeric(*this.squint,*key,size=#Squint_Integer,bhash=0)
    
    Protected *node.squint_Node,idx,offset,nodecount,vchar.i,vret.i,count,*akey.Ascii,hash.q,st  
    
    XCHG_(@*node,(*this\root & #Squint_Pmask))
    
    If bhash 
      *akey=*key 
      hash = $D45E69F901E72147 ! bhash;
      Repeat 
        hash = $3631754B22FF2D5C * (count + *akey\a) ! (hash << 2) ! (hash >> 2);
        *akey + 1
        count+1 
      Until count >= size 
      count = 0 
      size = 8
      *akey = @hash+(#Squint_Integer-1)
    Else 
      *akey = *key+(size-1)
    EndIf 
    
    While count <= size  
      
      l1:
      If Not (*node & 1) 
        
        XCHG_(@offset,*node\squint) 
        offset = (offset >> ((*akey\a & $f0) >> 2 )) & $f
        _GETNODECOUNT()
        If offset < nodecount
          XCHG_(@*node,(*node\Vertex\e[offset] & #Squint_Pmask))
        Else
          ProcedureReturn 0
        EndIf
      Else 
        Delay(0)
        Goto l1  
      EndIf  
      
      l2:
      If Not (*node & 1)  
        XCHG_(@offset,*node\squint) 
        offset = (offset >> ((*akey\a & $0f) << 2)) & $f
        _GETNODECOUNT()
        If offset < nodecount
          XCHG_(@*node,(*node\Vertex\e[offset] & #Squint_Pmask)) 
        Else
          ProcedureReturn 0
        EndIf
      Else 
        Delay(0)
        Goto l2  
      EndIf  
      *akey-1
      count+1
    Wend
    
    ProcedureReturn *node\value
  EndProcedure
  
  ;##################################################################################
  ;#  Delete Numeric resets the keys value to 0      
  ;#    *this.squint instance from SquintNew() 
  ;#    *key   address of a variable or memory pointer 
  ;#    size   number of bytes used to store the key    
  ;#  example  
  ;#     key.l = 12345  
  ;#     x = SquintDeleteNumeric(sq,@ikey,4)   
  ;#     or via interface 
  ;#     x = sq\DeleteNumeric(@ikey,4)      
  ;################################################################################## 
  
  Procedure SquintDeleteNumeric(*this.squint,*key,size=#Squint_Integer,bhash=0)    
    Protected *node.squint_node,idx,*mem.Character,offset,nodecount,vchar.i,vret.i,count,*akey.Ascii,hash.q 
    *node = *this\root & #Squint_Pmask
    
    If bhash 
      *akey=*key 
      hash = $D45E69F901E72147 ! bhash;
      Repeat 
        hash = $3631754B22FF2D5C * (count + *akey\a) ! (hash << 2) ! (hash >> 2);
        *akey + 1
        count+1 
      Until count >= size 
      count = 0 
      size = 8
      *akey = @hash+(#Squint_Integer-1)
    Else 
      *akey = *key+(size-1)
    EndIf 
    
    While count <= size 
      offset = (*node\squint >> ((*akey\a & $f0) >> 2 )) & $f
      _GETNODECOUNT()
      If offset < nodecount
        *node = (*node\Vertex\e[offset] & #Squint_Pmask)
      Else
        ProcedureReturn 0
      EndIf
      offset = (*node\squint >> ((*akey\a & $0f) << 2)) & $f
      _GETNODECOUNT()
      If offset < nodecount
        *node = (*node\Vertex\e[offset] & #Squint_Pmask)
      Else
        ProcedureReturn 0
      EndIf
      *akey-1
      count+1
    Wend
    If (*node\vertex & #Squint_Pmask) = 0
      *node\squint = 0
    EndIf
  EndProcedure
  
  Procedure IEnumNumeric(*this.squint,*node.squint_Node,depth,*pfn.squint_CB,*outkey.integer,size,*userdata=0)
    Protected a.i,offset,nodecount,*mem.Ascii,vchar.i,vret.i 
    
    If Not *node
      ProcedureReturn 0
    EndIf
    
    For a = 0 To 15 
      offset = (*node\squint >> (a<<2)) & $f
      If (*node\vertex And *node\squint)
        _GETNODECOUNT()
        If (offset <> 15 Or nodecount = 16)
          _POKENHL(*outkey,depth,a)
          IEnumNumeric(*this,*node\Vertex\e[offset] & #Squint_Pmask,depth+1,*pfn,*outkey,size,*userdata)
        EndIf
      EndIf
    Next
    
    If *node\vertex=0
      vchar = PeekI(*outkey) 
      CompilerIf #PB_Compiler_Backend = #PB_Backend_C 
        CompilerIf #PB_Compiler_Processor = #PB_Processor_x64  
          !v_vchar = __builtin_bswap64(v_vchar);    
        CompilerElse 
          !v_vchar = __builtin_bswap32(v_vchar);     
        CompilerEndIf
      CompilerElse 
        EnableASM
        mov rax, vchar
        bswap rax;
        mov vchar,rax
        DisableASM 
      CompilerEndIf 
      
      CompilerIf #PB_Compiler_Processor = #PB_Processor_x64  
        If size = 4 
          vchar >> 32 
        EndIf
      CompilerEndIf   
      
      If *node\value
        If *pfn   
          *pfn(@vchar,*node\value,*userdata)
        EndIf
      EndIf 
    EndIf
    
    ProcedureReturn *node
  EndProcedure
  
  ;##################################################################################
  ;# Walks whole trie. note it's not thread safe yet you can only walk one thread at a time with same trie    
  ;#    *this.squint instance from SquintNew() 
  ;#    *pfn.squint_CB address of callback function as Squint_CB(*key,*value=0,*userdata=0) 
  ;#     where *key is pointer to the key *value is pointer to the *value, *userDate      
  ;# example    
  ;#     squintWalkNumeric(sq,@MyCallback())       
  ;#  or via interface 
  ;#     sq\WalkNumeric(@MyCallback())   
  ;################################################################################## 
  
  Procedure SquintWalkNumeric(*this.squint,*pfn.squint_CB,size=#Squint_Integer,*userdata=0)       
    
    Protected depth,*node.squint_node,*new.squint,*old.squint,out.i,outkey.s{#SQUINT_MAX_KEY}        
    
    _LockMutex(*this\menum)
    
    *new = SquintNew()     
     
    XCHG_(@*this\merge,*new) 
    
    *node = *this\root & #Squint_Pmask
       
    CompilerIf #PB_Compiler_Processor = #PB_Processor_x64  
      If size > 8 
        IEnum(*this,*node,0,*pfn,@outkey,*userdata)
      Else 
        IEnumNumeric(*this,*node,depth,*pfn,@out,size,*userdata)
      EndIf   
    CompilerElse 
      If size > 4 
        IEnum(*this,*node,0,*pfn,@outkey,*userdata)
      Else 
        IEnumNumeric(*this,*node,depth,*pfn,@out,size,*userdata)
      EndIf   
      
    CompilerEndIf  
    
    _LockMutex(*this\mwrite)  
       
    *old = *this\merge
    XCHG_(@*this\merge,0)               
    SquintMerge(*old,*this,0,1) 
    
    _UnlockMutex(*this\mwrite)
           
    SquintFree(*old)
    
    _UnlockMutex(*this\menum) 
    
  EndProcedure
  
Post Reply