Resource Number Manager

Share your advanced PureBasic knowledge/code with the community.
dmoc
Enthusiast
Enthusiast
Posts: 739
Joined: Sat Apr 26, 2003 12:40 am

Resource Number Manager

Post by dmoc »

Code updated for 5.20+

I never posted this before because I thought #PB_Any would make it redundant. It's simple but efffective and easily extended for more types of resources. Note that there are obvious improvements to be done but this was a quick hack a couple of months back and it's done me ok up to now. I'll probably improve it in the next few weeks but feel free to do any yourself.

Code: Select all

 ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;*** RESOURCE NUMBER MANAGER ***
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 040315 REVISE IN FUTURE TO USE MB'S INSTEAD OF STRINGS
; AND POSS ABILITY TO DEFINE NEW TYPES ON THE FLY (WOULD
; HAVE TO RESERVE X MEM BANK NUMBERS + SPARE FOR NEW TYPES
;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

;-ENUMS

; NB: Add to this list if required
; (idea is to keep rtype=listindex() for easy mgmnt)
Enumeration ; Resource Types
  #RNM_TYPE_FILE
  #RNM_TYPE_IMAGE
  #RNM_TYPE_MEMBK
  ;#RNM_TYPE_
  #RNM_TYPE_END ; This is just a marker for initialisation
EndEnumeration

;-STRUCTS

Structure rnmItemType
  rtype.l ; Resource Type (see enumeration above)
  asize.l ; Size of string (since initially filling with zero's)
  alloc.s ; Space for bit flags (may grow but won't shrink!)
  used.l  ; Allocated count (to save computing on-the-fly)
EndStructure
Structure rnmT
  mid.l ; Flag: Main Init Done (so can be called again)
  dbg.l ; Flag: Debugging
EndStructure

;-VARS

Global rnm.rnmT
Global NewList rnmItems.rnmItemType()

Procedure rnmInit(maxnum.l)
  ; Init list with space for "maxnum" numbers in each item
  Protected t.l
 
  If rnm\mid: ProcedureReturn: EndIf
 
  For t=0 To #RNM_TYPE_END-1
    AddElement(rnmItems())
    rnmItems()\rtype=t
    rnmItems()\asize=1+maxnum/8
    rnmItems()\alloc=Space(rnmItems()\asize)
    ;rnmItems()\alloc=ReplaceString(rnmItems()\alloc, " ", Chr(0), 2) ; Zero
    RtlZeroMemory_(@rnmItems()\alloc, rnmItems()\asize)
    rnmItems()\used=0
  Next
  rnm\mid=1
EndProcedure
Procedure.l rnmNewNum(rtype.l)
  ; Return a new resource number for specified resource type
  Protected a.l, c.l, b.l, d.l, m.b, n.l
 
  SelectElement(rnmItems(), rtype)
 
  ; All avail allocated?
  If rnmItems()\used = 8*rnmItems()\asize ; All bits allocated
    Debug "RNM: Max'ed out: "+Str(rtype)
    CallDebugger
    ProcedureReturn -1
  EndIf

  ; Allocate first free number
  a = @rnmItems()\alloc
  For c = 0 To rnmItems()\asize-1
    d = PeekB(a+c) & $FF
    If d<>255 ; Found unallocated number
      For b = 0 To 7
        m = 1 << b: If (d & m)=0: Break 2: EndIf
      Next
    EndIf
  Next
 
  PokeB(a+c, d | m)
  rnmItems()\used+1
  n = 8*c+b
 
  ProcedureReturn n
EndProcedure
Procedure rnmFreeNum(rtype.l, n.l)
  ; Free resource number for specified resource type
  Protected a.l, c.l, b.l, m.l
 
  SelectElement(rnmItems(), rtype)
 
  c = n/8
  b = n - c*8
  m = (~(1 << b)) & $FF
 
  a = @rnmItems()\alloc
  PokeB(a+c, PeekB(a+c) & m)
  rnmItems()\used-1
EndProcedure
Procedure rnmTest()
  rnmInit(512)
 
  Debug "Allocate several of type #RNM_TYPE_IMAGE (0..9)..."
  Debug rnmNewNum(#RNM_TYPE_IMAGE) ; 0
  Debug rnmNewNum(#RNM_TYPE_IMAGE) ; 1
  Debug rnmNewNum(#RNM_TYPE_IMAGE) ; 2
  Debug rnmNewNum(#RNM_TYPE_IMAGE) ; 3
  Debug rnmNewNum(#RNM_TYPE_IMAGE) ; 4
  Debug rnmNewNum(#RNM_TYPE_IMAGE) ; 5
  Debug rnmNewNum(#RNM_TYPE_IMAGE) ; 6
  Debug rnmNewNum(#RNM_TYPE_IMAGE) ; 7
  Debug rnmNewNum(#RNM_TYPE_IMAGE) ; 8
  Debug rnmNewNum(#RNM_TYPE_IMAGE) ; 9
  Debug ""
  Debug "Free # 3"
  rnmFreeNum(#RNM_TYPE_IMAGE, 3) ; Should get re-allocated on next call
  Debug ""
  Debug "Allocate several more of type #RNM_TYPE_IMAGE (3, 10, 11)..."
  Debug rnmNewNum(#RNM_TYPE_IMAGE) ; 3
  Debug rnmNewNum(#RNM_TYPE_IMAGE) ; 10 (because 4..9 already allocated)
  Debug rnmNewNum(#RNM_TYPE_IMAGE) ; 11
  Debug ""
  Debug "Allocate several of other type #RNM_TYPE_FILE (0..3)..."
  Debug rnmNewNum(#RNM_TYPE_FILE) ; 0
  Debug rnmNewNum(#RNM_TYPE_FILE) ; 1
  Debug rnmNewNum(#RNM_TYPE_FILE) ; 2
  Debug rnmNewNum(#RNM_TYPE_FILE) ; 3
  Debug ""

  Debug "Usage, (type|used)..."
  ResetList(rnmItems())
  While NextElement(rnmItems())
    Debug Str(rnmItems()\rtype)+": "+Str(rnmItems()\used)
  Wend
 
EndProcedure

rnmTest()

User avatar
blueznl
PureBasic Expert
PureBasic Expert
Posts: 6172
Joined: Sat May 17, 2003 11:31 am
Contact:

Post by blueznl »

or, very simple...

Code: Select all

Procedure.l x_nr()
  Global x_nr.l, x_nr_n.l
  ;
  ; *** generates a new unique number
  ;
  ; in:      none
  ; retval:  unused unique number
  ;
  ; pure sometimes uses 'numbers' to identify elements instead of windows handles
  ; these numbers are arbitrary and fully defined by the user, this can cause trouble
  ; when using multiple libraries or code from different people as they might be reusing
  ; these unique numbers, for this purpose, i've added a x_nr procedure that returns
  ; a new unique number on every call
  ;
  ; you decide yourself if you want to free them or not :-)
  ; i would suggest doing so if you don't 'recycle' them using x_freenr() because other procedures
  ; may end up with numbers that are 'too high' for the pb functions being called
  ;
  If CountList(x_nr_list()) > 0         ; which means there's some stuff on the list
    x_nr = x_nr_list()                  ; then use that number
    DeleteElement(x_nr_list())          ; and take if from the list
  Else
    x_nr = x_nr_n
    x_nr_n = x_nr_n+1
  EndIf
  ProcedureReturn x_nr
EndProcedure

Procedure x_freenr(n.l)
  ;
  ; *** recycles unique numbers
  ;
  ; put this number into the 'free numbers' list so it can be reused by x_nr()
  ;
  AddElement(x_nr_list())
  x_nr_list()=n
  ;
EndProcedure
( PB6.00 LTS Win11 x64 Asrock AB350 Pro4 Ryzen 5 3600 32GB GTX1060 6GB - upgrade incoming...)
( The path to enlightenment and the PureBasic Survival Guide right here... )
User avatar
blueznl
PureBasic Expert
PureBasic Expert
Posts: 6172
Joined: Sat May 17, 2003 11:31 am
Contact:

Post by blueznl »

or, very simple...

Code: Select all

Procedure.l x_nr()
  Global x_nr.l, x_nr_n.l
  ;
  ; *** generates a new unique number
  ;
  ; in:      none
  ; retval:  unused unique number
  ;
  ; pure sometimes uses 'numbers' to identify elements instead of windows handles
  ; these numbers are arbitrary and fully defined by the user, this can cause trouble
  ; when using multiple libraries or code from different people as they might be reusing
  ; these unique numbers, for this purpose, i've added a x_nr procedure that returns
  ; a new unique number on every call
  ;
  ; you decide yourself if you want to free them or not :-)
  ; i would suggest doing so if you don't 'recycle' them using x_freenr() because other procedures
  ; may end up with numbers that are 'too high' for the pb functions being called
  ;
  If CountList(x_nr_list()) > 0         ; which means there's some stuff on the list
    x_nr = x_nr_list()                  ; then use that number
    DeleteElement(x_nr_list())          ; and take if from the list
  Else
    x_nr = x_nr_n
    x_nr_n = x_nr_n+1
  EndIf
  ProcedureReturn x_nr
EndProcedure

Procedure x_freenr(n.l)
  ;
  ; *** recycles unique numbers
  ;
  ; put this number into the 'free numbers' list so it can be reused by x_nr()
  ;
  AddElement(x_nr_list())
  x_nr_list()=n
  ;
EndProcedure
( PB6.00 LTS Win11 x64 Asrock AB350 Pro4 Ryzen 5 3600 32GB GTX1060 6GB - upgrade incoming...)
( The path to enlightenment and the PureBasic Survival Guide right here... )
dmoc
Enthusiast
Enthusiast
Posts: 739
Joined: Sat Apr 26, 2003 12:40 am

Post by dmoc »

Very good but what about *different* resource types... more lists? I avoided lists because there was/is a memory leak with them. Also the list overhead is 8 bytes per number. Not a problem for most programs but I'm potentially using 1000's of unique numbers. Keep the suggestions coming though because it's interesting seeing other peoples solutions :D

Clarification: Of course I'm using a list but not for the actual numbers
User avatar
blueznl
PureBasic Expert
PureBasic Expert
Posts: 6172
Joined: Sat May 17, 2003 11:31 am
Contact:

Post by blueznl »

dmoc, why not keep using the same list? who cares if you skip numbers in a list?

if the linked list has memory leaks, one could resort to an array, just as easy...
( PB6.00 LTS Win11 x64 Asrock AB350 Pro4 Ryzen 5 3600 32GB GTX1060 6GB - upgrade incoming...)
( The path to enlightenment and the PureBasic Survival Guide right here... )
dmoc
Enthusiast
Enthusiast
Posts: 739
Joined: Sat Apr 26, 2003 12:40 am

Post by dmoc »

why not keep using the same list? who cares if you skip numbers in a list?
Long time ago I asked Fred the implication of say reserving mem banks 0..999 for object type x, 1000..1999 for object type, etc, but where 0..999 was unused. The question was: is there wasted resources if I only used mem bank # 1000 (or what about 10000?). The answer: yes. So to avoid waste ideally you use consecutive numbers. Don't know if this is still true. Also, same list implies same resource but numbers for mem banks, files, images, etc are best kept seperate.
if the linked list has memory leaks, one could resort to an array
In effect I am using an array but the difference is I'm only storing the fact that the number is in-use and for this only one bit is required.

In the end it's horses-for-courses. For simple use (no sarcasm/ criticism intended) your code would do just fine. My code is extracted from a large-ish app doing real-time (-ish :P ) 3D and needs to handle other things identified by a unique number within a specific class. As an example, in opengl it's generally a bad idea to keep querying opengl to see if a particular display list is in use, or a texture obj, etc, so my code allows for me to add another resource type to represent these and avoid querying opengl altogether (ok, for the opengl experts among you I know it's not infallible, ogl can drop tex objs, etc, but it works ok at the mo :P )
Post Reply