unit kern_id; {$mode ObjFPC}{$H+} {$CALLING SysV_ABI_CDecl} interface uses hamt, kern_rwlock; Const def_max_key=$7FFFFFFF; type p_id_desc=^t_id_desc; t_id_desc=packed record free:procedure(data:pointer); refs:Integer; end; p_id_desc_table=^t_id_desc_table; t_id_desc_table=packed record FLock :Pointer; //table lock FHAMT :TSTUB_HAMT32; //hamt by id FCount :Integer; //count alloc id FSpace :Integer; //count free id FLast :Integer; //last free id FPos :Integer; //max uses pos min_key:Integer; //min key [min_key,max_key) max_key:Integer; //max key [min_key,max_key) end; t_filter_cb=function(d:p_id_desc):Boolean of object; Procedure id_acqure (d:p_id_desc); Procedure id_release(d:p_id_desc); procedure id_table_init(t:p_id_desc_table;min:Integer;max:Integer=def_max_key); procedure id_table_fini(t:p_id_desc_table); function id_new(t:p_id_desc_table;d:p_id_desc;pKey:PInteger):Boolean; function id_set(t:p_id_desc_table;d:p_id_desc;Key:Integer;old:PPointer):Boolean; function id_get(t:p_id_desc_table;Key:Integer;cb:t_filter_cb=nil):p_id_desc; function id_del(t:p_id_desc_table;Key:Integer;old:PPointer;cb:t_filter_cb=nil):Boolean; implementation Procedure id_acqure(d:p_id_desc); public; begin if (d=nil) then Exit; System.InterlockedIncrement(d^.refs); end; Procedure id_release(d:p_id_desc); public; begin if (d=nil) then Exit; Assert(d^.refs>0); if (System.InterlockedDecrement(d^.refs)=0) then if (d^.free<>nil) then begin d^.free(d); end; end; procedure _free_data_cb(data,userdata:Pointer); register; begin if (data<>nil) then begin id_release(data); end; end; procedure id_table_init(t:p_id_desc_table;min:Integer;max:Integer=def_max_key); public; begin if (t=nil) then Exit; FillChar(t^,SizeOf(t_id_desc_table),0); if (min>max) then begin t^.min_key:=max; t^.max_key:=min; end else begin t^.min_key:=min; t^.max_key:=max; end; t^.FPos :=t^.min_key; t^.FLast:=-1; end; procedure id_table_fini(t:p_id_desc_table); public; begin if (t=nil) then Exit; HAMT_clear32(@t^.FHAMT,@_free_data_cb,nil); end; function id_new(t:p_id_desc_table;d:p_id_desc;pKey:PInteger):Boolean; public; Label _insert, _exit; Var i:Integer; data:PPointer; begin Result:=False; if (t=nil) or (pKey=nil) then Exit; rw_wlock(t^.FLock); i:=(t^.max_key-t^.min_key); if (t^.FCount>=i) then goto _exit; //limit reached if (t^.FPost^.max_key) then begin t^.FPos:=t^.max_key; //fixup end; if (t^.FSpace<>0) then begin if (t^.FPos=t^.min_key) then begin t^.FSpace:=0; //fixup end else if (t^.FLast=-1) or //no last free ((t^.FLast1)) then begin //find space id (not an efficient linear search) For i:=t^.min_key to t^.FPos-1 do begin if (i<>t^.FLast) then //not last free begin if (HAMT_search32(@t^.FHAMT,i)=nil) then begin //found if (t^.FSpace<>0) then begin Dec(t^.FSpace); end; goto _insert; end; end; end; //not found end; end; i:=t^.FPos; if (i=t^.max_key) then //limit begin //check last free i:=t^.FLast; if (i=-1) then goto _exit; if (t^.FSpace<>0) then begin Dec(t^.FSpace); end; end else begin Inc(t^.FPos); if (i=t^.FLast) and //last free? (t^.FPosPointer(d)) then goto _exit; //wtf Inc(t^.FCount); if (i=t^.FLast) then begin //reset last free t^.FLast:=-1; end; id_acqure(d); //table ref id_acqure(d); //ref for further use Result:=True; _exit: rw_wunlock(t^.FLock); end; function id_set(t:p_id_desc_table;d:p_id_desc;Key:Integer;old:PPointer):Boolean; Label _exit; Var data:PPointer; begin Result:=False; if (t=nil) or (Keyt^.max_key) then Exit; rw_wlock(t^.FLock); data:=HAMT_insert32(@t^.FHAMT,Key,Pointer(d)); if (data=nil) then goto _exit; //nomem if (data^<>Pointer(d)) then //another begin if (old<>nil) then //swap begin old^:=data^; data^:=Pointer(d); end else begin goto _exit; //not set end; end else begin //new if (old<>nil) then begin old^:=nil; //not old value end; // Inc(t^.FCount); if (Key=t^.FLast) then begin //reset last free t^.FLast:=-1; end; if (Key0) then begin Dec(t^.FSpace); end; end; end; id_acqure(d); //table ref Result:=True; _exit: rw_wunlock(t^.FLock); end; function id_get(t:p_id_desc_table;Key:Integer;cb:t_filter_cb=nil):p_id_desc; Var data:PPointer; Label _exit; begin Result:=nil; if (t=nil) or (Keyt^.max_key) then Exit; rw_rlock(t^.FLock); data:=HAMT_search32(@t^.FHAMT,Key); if (data=nil) then Goto _exit; //not found Pointer(Result):=data^; if (cb<>nil) then begin if not cb(Result) then Goto _exit; //filtred end; id_acqure(Result); //ref for further use _exit: rw_runlock(t^.FLock); end; function id_del(t:p_id_desc_table;Key:Integer;old:PPointer;cb:t_filter_cb=nil):Boolean; Var data:PPointer; d,rel:p_id_desc; p:Integer; Label _exit; begin Result:=False; if (t=nil) or (Keyt^.max_key) then Exit; d:=nil; rel:=nil; rw_wlock(t^.FLock); if (cb<>nil) then begin data:=HAMT_search32(@t^.FHAMT,Key); if (data=nil) then Goto _exit; //not found d:=data^; if not cb(d) then Goto _exit; //filtred d:=nil; end; if HAMT_delete32(@t^.FHAMT,Key,@d) then begin //deleted if (old<>nil) then begin old^:=d; end else begin rel:=d; end; Dec(t^.FCount); t^.FLast:=Key; p:=t^.FPos-1; if (p=Key) and (p>=t^.min_key) and (p<=t^.max_key) then begin //dec max uses t^.FPos:=p; Dec(p); while (p>=t^.min_key) do begin if (HAMT_search32(@t^.FHAMT,p)<>nil) then Break; //found //dec space if (t^.FSpace<>0) then begin Dec(t^.FSpace); end; t^.FPos:=p; Dec(p); end; end else begin //insert space Inc(t^.FSpace); end; Result:=True; end else if (old<>nil) then begin //not found old^:=nil; end; _exit: rw_wunlock(t^.FLock); // id_release(rel); end; end.