summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: 84fc423)
raw | patch | inline | side by side (parent: 84fc423)
author | Ketmar Dark <ketmar@ketmar.no-ip.org> | |
Fri, 25 Aug 2017 12:58:49 +0000 (15:58 +0300) | ||
committer | Ketmar Dark <ketmar@ketmar.no-ip.org> | |
Fri, 25 Aug 2017 13:01:46 +0000 (16:01 +0300) |
src/shared/idpool.pas | patch | blob | history | |
src/shared/ztest_idpool.dpr | patch | blob | history |
diff --git a/src/shared/idpool.pas b/src/shared/idpool.pas
index efba5e6f5cadcee282c7be5ff9a4cf7a23888af7..7817cc160b259ff91a7eef539205e1276ed1b746 100644 (file)
--- a/src/shared/idpool.pas
+++ b/src/shared/idpool.pas
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*)
{$INCLUDE a_modes.inc}
-{.$DEFINE IDPOOL_CHECKS}
+{$DEFINE IDPOOL_CHECKS}
unit idpool;
interface
// returns InvalidId if there are no more free ids (or throws)
function alloc (dothrow: Boolean=true): LongWord;
+ // returns InvalidId if there are no more free ids (or throws)
+ function alloc (aid: LongWord; dothrow: Boolean=true): LongWord;
+
// it is NOT ok to release already released id
procedure release (aid: LongWord);
property capacity: Integer read getCapacity;
end;
-
implementation
uses
begin
for f := 0 to mRangeUsed-1 do
begin
- if (mRanges[f].first > mRanges[f].last) then raise Exception.Create('invalid range');
- if (mRanges[f].first > mMaxId) then raise Exception.Create('invalid range');
- if (mRanges[f].last > mMaxId) then raise Exception.Create('invalid range');
- if (f > 0) and (mRanges[f-1].last >= mRanges[f].first) then raise Exception.Create('invalid range order');
- if (f > 0) and (mRanges[f-1].last+1 = mRanges[f].first) then raise Exception.Create('unmerged ranges');
+ if (mRanges[f].first > mRanges[f].last) then begin dump(); raise Exception.Create('invalid range'); end;
+ if (mRanges[f].first > mMaxId) then begin dump(); raise Exception.Create('invalid range'); end;
+ if (mRanges[f].last > mMaxId) then begin dump(); raise Exception.Create('invalid range'); end;
+ if (f > 0) and (mRanges[f-1].last >= mRanges[f].first) then begin dump(); raise Exception.Create('invalid range order'); end;
+ if (f > 0) and (mRanges[f-1].last+1 = mRanges[f].first) then begin dump(); raise Exception.Create('unmerged ranges'); end;
end;
end;
end;
+// returns InvalidId if there are no more free ids (or throws)
+function TIdPool.alloc (aid: LongWord; dothrow: Boolean=true): LongWord;
+var
+ ii, c: Integer;
+begin
+ if (mRangeUsed = 0) then
+ begin
+ // no more ids
+ if dothrow then raise Exception.Create('TIdPool: no more free ids');
+ result := InvalidId;
+ exit;
+ end;
+ // invalid?
+ if (aid > mMaxId) then
+ begin
+ if dothrow then raise Exception.Create('TIdPool: cannot allocate invalid id');
+ result := InvalidId;
+ exit;
+ end;
+ // find range with this id
+ ii := findRangeWithId(aid);
+ if (ii < 0) or (aid < mRanges[ii].first) or (aid > mRanges[ii].last) then
+ begin
+ if dothrow then raise Exception.Create('TIdPool: cannot allocate already allocated id');
+ result := InvalidId;
+ exit;
+ end;
+ // always return requested id
+ result := aid;
+ // can we shrink range head?
+ if (aid = mRanges[ii].first) then
+ begin
+ // yep; range with the only id?
+ if (aid = mRanges[ii].last) then
+ begin
+ // delete this range
+ for c := ii+1 to mRangeUsed-1 do mRanges[c-1] := mRanges[c];
+ Dec(mRangeUsed);
+ end
+ else
+ begin
+ mRanges[ii].first := aid+1;
+ end;
+ Inc(mUsedIds);
+ {$IF DEFINED(IDPOOL_CHECKS)}check();{$ENDIF}
+ exit;
+ end;
+ // can we shrink range tail?
+ if (aid = mRanges[ii].last) then
+ begin
+ // yep; simply shrink, 'cause range with one id was processed in the previous `if`
+ mRanges[ii].last := aid-1;
+ Inc(mUsedIds);
+ {$IF DEFINED(IDPOOL_CHECKS)}check();{$ENDIF}
+ exit;
+ end;
+ // split this range to two
+ if (mRangeUsed+1 > Length(mRanges)) then SetLength(mRanges, Length(mRanges)+1024);
+ for c := mRangeUsed downto ii+1 do mRanges[c] := mRanges[c-1];
+ Inc(mRangeUsed);
+ mRanges[ii].last := aid-1;
+ mRanges[ii+1].first := aid+1;
+ Inc(mUsedIds);
+ {$IF DEFINED(IDPOOL_CHECKS)}check();{$ENDIF}
+end;
+
+
// it is NOT ok to release already released id
procedure TIdPool.release (aid: LongWord);
var
else
begin
// nope, insert new first range
- if (mRangeUsed+1 > Length(mRanges)) then SetLength(mRanges, Length(mRanges)*2);
+ if (mRangeUsed+1 > Length(mRanges)) then SetLength(mRanges, Length(mRanges)+1024);
assert(mRangeUsed < Length(mRanges));
for c := mRangeUsed downto 1 do mRanges[c] := mRanges[c-1];
Inc(mRangeUsed);
else
begin
// nope, insert new last range
- if (mRangeUsed+1 > Length(mRanges)) then SetLength(mRanges, Length(mRanges)*2);
+ if (mRangeUsed+1 > Length(mRanges)) then SetLength(mRanges, Length(mRanges)+1024);
assert(mRangeUsed < Length(mRanges));
mRanges[mRangeUsed].first := aid;
mRanges[mRangeUsed].last := aid;
exit;
end;
// cannot grow anything, insert empty range after ii
- if (mRangeUsed = Length(mRanges)) then SetLength(mRanges, Length(mRanges)*2);
+ if (mRangeUsed = Length(mRanges)) then SetLength(mRanges, Length(mRanges)+1024);
for c := mRangeUsed downto ii do mRanges[c+1] := mRanges[c];
Inc(ii);
mRanges[ii].first := aid;
index 393c43cf1c34fae1d27b008cc32eadb419d13de8..32d0fd05540476db2ac05aee79e34808360e057a 100644 (file)
f, n: Integer;
usedIds: Integer = 0;
begin
- ip := TIdPool.Create(65535*1024);
+ ip := TIdPool.Create(65535);
SetLength(map, ip.maxId+1);
for f := 0 to High(map) do map[f] := false;
for f := 0 to High(map) div 2 do
begin
+ while true do
+ begin
+ n := Random(ip.maxId+1);
+ if map[n] then
+ begin
+ if not ip.hasAlloced[n] then raise Exception.Create('invalid pool(0)');
+ if ip.hasFree[n] then raise Exception.Create('invalid pool(1)');
+ continue;
+ end;
+ break;
+ end;
+ if (ip.alloc(n) <> n) then raise Exception.Create('wutafuuuuu?!');
+ map[n] := true;
+ Inc(usedIds);
+ if not ip.hasAlloced[n] then raise Exception.Create('invalid pool(3)');
+ if ip.hasFree[n] then raise Exception.Create('invalid pool(4)');
+ //ip.dump();
+ {
if ip.hasAlloced[f] then raise Exception.Create('invalid pool(0)');
if not ip.hasFree[f] then raise Exception.Create('invalid pool(1)');
if (ip.alloc <> f) then raise Exception.Create('invalid alloc(2)');
Inc(usedIds);
if not ip.hasAlloced[f] then raise Exception.Create('invalid pool(3)');
if ip.hasFree[f] then raise Exception.Create('invalid pool(4)');
+ }
end;
for f := 0 to 10000000 do
begin
if not ip.hasFree[f] then raise Exception.Create('invalid pool(e)');
end;
end;
+ ip.Free();
end;