mirror of https://github.com/red-prig/fpPS4.git
368 lines
6.3 KiB
Plaintext
368 lines
6.3 KiB
Plaintext
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^.FPos<t^.min_key) then
|
|
begin
|
|
t^.FPos:=t^.min_key; //fixup
|
|
end;
|
|
|
|
if (t^.FPos>t^.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^.FLast<t^.FPos) and (t^.FSpace<>1)) 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^.FPos<t^.max_key) then //bound?
|
|
begin
|
|
//skip last free
|
|
i:=t^.FPos;
|
|
Inc(t^.FSpace);
|
|
Inc(t^.FPos);
|
|
end;
|
|
end;
|
|
|
|
_insert:
|
|
pKey^:=i;
|
|
data:=HAMT_insert32(@t^.FHAMT,pKey^,Pointer(d));
|
|
|
|
if (data=nil) then goto _exit; //nomem
|
|
if (data^<>Pointer(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 (Key<t^.min_key) or (Key>t^.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 (Key<t^.FPos) then
|
|
begin
|
|
//in the space of use
|
|
if (t^.FSpace<>0) 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 (Key<t^.min_key) or (Key>t^.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 (Key<t^.min_key) or (Key>t^.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.
|
|
|