1 (* Copyright (C) Doom 2D: Forever Developers
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, version 3 of the License ONLY.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 {$INCLUDE a_modes.inc}
16 {$DEFINE MEM_DISABLE_ACCOUNTING}
31 mObjSize
: Integer; // not a limit, just a recommendation
38 constructor Create (const aname
: AnsiString; aobjsize
: Integer);
40 procedure setCapacity (acount
: Integer); // ensure capacity for at least `acount` objects
41 procedure release (); // release all pool memory
43 function alloc (len
: Integer): Pointer; // throws on OOM
44 procedure free (ptr
: Pointer); // currently it is noop
47 property name
: ShortString read mName
;
48 property allocCount
: Integer read mAllocCount
;
49 property allocTotal
: Integer read mAllocTotal
;
54 {$IF DEFINED(D2F_DEBUG) and NOT DEFINED(MEM_DISABLE_ACCOUNTING)}
55 public class function NewInstance (): TObject
; override;
56 public procedure FreeInstance (); override;
61 (* Simple "mark/release" allocator *)
65 PPoolMarkRelease
= ^TPoolMarkRelease
;
66 TPoolMarkRelease
= record
73 constructor Create (aInitSize
: Integer);
75 // free all allocated memory
81 // mark current position
82 function mark (): PoolMark
; inline;
83 // forget everything from the given mark
84 procedure release (amark
: PoolMark
); inline;
86 // allocate some memory
87 // WARNING! pool can realloc it's internal storage and invalidate all previous pointers!
88 function alloc (size
: Integer): Pointer; inline;
90 // get pointer for the given mark
91 // WARNING! pointer can become invalid after next call to `alloc()`!
92 function getPtr (amark
: PoolMark
): Pointer; inline;
93 function curPtr (): Pointer; inline;
98 generic PoolIter
<T
> = record
101 type MyType
= specialize PoolIter
<T
>;
104 mPool
: PPoolMarkRelease
;
111 constructor Create (var apool
: TPoolMarkRelease
); // idiotic FPC doesn't support arg-less ctors for rectord
112 procedure finishIt (); inline; // sets count
114 procedure rewind (); inline;
115 function length (): Integer; inline;
116 procedure release (); inline; // reset pool
118 function moveNext (): Boolean; inline;
119 function getCurrent (): Ptr
; inline;
120 function getEnumerator (): MyType
; inline;
122 function first (): Ptr
; inline;
125 property current
: Ptr read getCurrent
;
130 framePool
: TPoolMarkRelease
; // temporary per-frame allocation pool
143 // ////////////////////////////////////////////////////////////////////////// //
144 constructor TPoolMarkRelease
.Create (aInitSize
: Integer);
146 if (aInitSize
> 0) then
149 GetMem(mMemory
, mSize
);
160 // free all allocated memory
161 procedure TPoolMarkRelease
.kill ();
163 if (mMemory
<> nil) then FreeMem(mMemory
);
171 procedure TPoolMarkRelease
.reset ();
177 // mark current position
178 function TPoolMarkRelease
.mark (): PoolMark
; inline;
184 // forget everything from the given mark
185 procedure TPoolMarkRelease
.release (amark
: PoolMark
); inline;
187 if (amark
< 0) or (amark
> mUsed
) then raise Exception
.Create('MarkReleasePool is fucked (release)');
192 // allocate some memory
193 // WARNING! pool can realloc it's internal storage and invalidate all previous pointers!
194 function TPoolMarkRelease
.alloc (size
: Integer): Pointer; inline;
196 if (size
< 0) then raise Exception
.Create('MarkReleasePool: cannot allocate negative amount of bytes');
197 if (size
> 1024*1024) then raise Exception
.Create('MarkReleasePool: why do you need to allocate more than 1MB?');
198 // do we need to get more memory?
199 if (mUsed
+size
> mSize
) then
201 if (mUsed
+size
> 1024*1024*64) then raise Exception
.Create('MarkReleasePool: more than 64MB in MarkReleasePool is insanity!');
202 while (mUsed
+size
> mSize
) do
204 // less than 256KB: 64KB steps
205 if (mSize
< 256*1024) then mSize
+= 64*1024
206 // less than 1MB: 128KB steps
207 else if (mSize
< 1024*1024) then mSize
+= 128*1024
208 // otherwise, 1MB steps
209 else mSize
+= 1024*1024;
211 ReallocMem(mMemory
, mSize
);
212 if (mMemory
= nil) then raise Exception
.Create('MarkReleasePool: out of memory!');
214 result
:= Pointer(PAnsiChar(mMemory
)+mUsed
);
216 assert(mUsed
<= mSize
);
220 // get pointer for the given mark
221 // WARNING! pointer can become invalid after next call to `alloc()`!
222 function TPoolMarkRelease
.getPtr (amark
: PoolMark
): Pointer; inline;
224 if (amark
< 0) or (amark
> mUsed
) then raise Exception
.Create('MarkReleasePool is fucked (getPtr)');
225 result
:= Pointer(PAnsiChar(mMemory
)+amark
);
229 function TPoolMarkRelease
.curPtr (): Pointer; inline;
231 result
:= Pointer(PAnsiChar(mMemory
)+mUsed
);
236 // ////////////////////////////////////////////////////////////////////////// //
237 constructor PoolIter
.Create (var apool
: TPoolMarkRelease
);
240 mMark
:= mPool
^.mark();
247 procedure PoolIter
.finishIt (); inline; // sets count
249 if (mFinished
) then raise Exception
.Create('double fatality');
250 if (mPool
= nil) then raise Exception
.Create('void fatality');
252 mCount
:= Integer(PtrUInt(mPool
^.curPtr
)-PtrUInt(mPool
^.getPtr(mMark
))) div Integer(sizeof(T
));
253 if (mCount
< 0) then raise Exception
.Create('wutafu?');
257 procedure PoolIter
.rewind (); inline;
259 if (mPool
= nil) then raise Exception
.Create('void rewind');
264 function PoolIter
.length (): Integer; inline;
266 //if (mCurrent+1 >= 0) and (mCurrent+1 < mCount) then result := mCount-(mCurrent+1) else result := 0;
271 procedure PoolIter
.release (); inline; // reset pool
273 if (mPool
= nil) then raise Exception
.Create('double release');
274 mPool
^.release(mMark
);
282 function PoolIter
.moveNext (): Boolean; inline;
284 if (mPool
= nil) then raise Exception
.Create('void moveNext()');
285 if (not mFinished
) then raise Exception
.Create('moveNext() on unfinished');
287 result
:= (mCurrent
< mCount
);
291 function PoolIter
.getCurrent (): Ptr
; inline;
293 if (mPool
= nil) then raise Exception
.Create('getCurrent() on nothing');
294 if (mCurrent
< 0) or (mCurrent
>= mCount
) then raise Exception
.Create('getCurrent() range error');
295 result
:= Ptr(mPool
^.getPtr(mMark
+mCurrent
*Integer(sizeof(T
))));
299 function PoolIter
.getEnumerator (): PoolIter
; inline;
305 function PoolIter
.first (): Ptr
; inline;
307 if (mPool
= nil) then raise Exception
.Create('void moveNext()');
308 if (not mFinished
) then raise Exception
.Create('moveNext() on unfinished');
309 result
:= Ptr(mPool
^.getPtr(mMark
));
313 // ////////////////////////////////////////////////////////////////////////// //
321 class function hash (const k
: Pointer): LongWord; inline;
322 class function equ (const a
, b
: Pointer): Boolean; inline;
323 class procedure freekey (k
: Pointer); inline;
326 THashPtrPtr
= specialize THashBase
<Pointer, PMemPool
, THashKeyPtr
>; // key: TClass; value: PMemPool
329 pools
: THashPtrPtr
= nil;
332 // ////////////////////////////////////////////////////////////////////////// //
333 class function THashKeyPtr
.hash (const k
: Pointer): LongWord; inline; begin result
:= fnvHash(PByte(@k
)^, sizeof(k
)); end;
334 class function THashKeyPtr
.equ (const a
, b
: Pointer): Boolean; inline; begin result
:= (a
= b
); end;
335 class procedure THashKeyPtr
.freekey (k
: Pointer); inline; begin end;
338 function getPoolFor (c
: TClass
): PMemPool
;
340 if (pools
= nil) then pools
:= THashPtrPtr
.Create();
341 if not pools
.get(Pointer(c
), result
) then
343 GetMem(result
, sizeof(TMemPool
));
344 result
.Create(c
.ClassName
, c
.InstanceSize
);
345 pools
.put(Pointer(c
), result
);
350 // ////////////////////////////////////////////////////////////////////////// //
351 constructor TMemPool
.Create (const aname
: AnsiString; aobjsize
: Integer);
353 if (aobjsize
< 1) then aobjsize
:= 16; // arbitrary number
355 mObjSize
:= aobjsize
;
363 procedure TMemPool
.setCapacity (acount
: Integer); // ensure capacity for at least `acount` objects
368 procedure TMemPool
.release (); // release all pool memory
373 function TMemPool
.alloc (len
: Integer): Pointer; // throws on OOM
375 if (len
> 0) then mAllocTotal
+= len
;
376 if (len
< 1) then len
:= 1;
378 FillChar(PByte(result
)^, len
, 0);
383 procedure TMemPool
.free (ptr
: Pointer); // currently it is noop
389 // ////////////////////////////////////////////////////////////////////////// //
390 {$IF DEFINED(D2F_DEBUG) and NOT DEFINED(MEM_DISABLE_ACCOUNTING)}
391 class function TPoolObject
.NewInstance (): TObject
;
393 {$IF DEFINED(D2F_DEBUG) and NOT DEFINED(MEM_DISABLE_ACCOUNTING)}
398 {$IF DEFINED(D2F_DEBUG) and NOT DEFINED(MEM_DISABLE_ACCOUNTING)}
399 pool
:= getPoolFor(self
.ClassType
);
400 ptr
:= pool
.alloc(self
.InstanceSize
);
402 GetMem(ptr
, self
.InstanceSize
);
403 FillChar(PByte(ptr
)^, self
.InstanceSize
, 0); // hello, Wyoming Knott!
405 result
:= TObject(ptr
);
406 self
.InitInstance(ptr
);
410 procedure TPoolObject
.FreeInstance ();
414 pool
:= getPoolFor(self
.ClassType
);
415 pool
.free(Pointer(self
));
420 // ////////////////////////////////////////////////////////////////////////// //
421 {$IF DEFINED(D2F_DEBUG) and NOT DEFINED(MEM_DISABLE_ACCOUNTING)}
422 procedure dumpPools ();
425 kv
: THashPtrPtr
.PEntry
;
427 AssignFile(fo
, 'zmemlog.txt');
429 for kv
in pools
.byKeyValue
do
431 writeln(fo
, kv
.value
.name
, ': count=', kv
.value
.allocCount
, '; total=', kv
.value
.allocTotal
);
439 //mpoolMap := TMemPool.Create('textmap', 64);
440 framePool
:= TPoolMarkRelease
.Create(65536);
442 {$IF DEFINED(D2F_DEBUG) and NOT DEFINED(MEM_DISABLE_ACCOUNTING)}
445 {$ENDIF} // USE_MEMPOOL