2 * coded by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
3 * Understanding is not required. Only obedience.
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 module iv
.ssarray
/*is aliced*/;
20 version(aliced
) {} else private alias usize
= size_t
;
22 //debug = debug_ssarray;
25 /** Array implementation that doesn't fragment memory, and does no `realloc()`s.
27 * I.e. `SSArray` will never move its items on resizing.
28 * Also, there is no need to "reserve" space in `SSArray`, it won't make your
29 * code faster, 'cause total number of `malloc()`s will be the same.
31 * $(WARNING This completely ignores any postblits, dtors and GC anchoring!)
33 static struct SSArray(T
, bool initNewElements
=true, uint MemPageSize
=4096)
34 if (MemPageSize
> 0 && MemPageSize
< uint.max
/65536 && T
.sizeof
<= MemPageSize
)
36 public nothrow @trusted @nogc:
37 enum PageSize
= MemPageSize
;
38 enum ItemsPerPage
= cast(uint)(PageSize
/T
.sizeof
); // items per one page
43 static struct PageStore
{
44 enum RefsPerPage
= cast(uint)(PageSize
/(void*).sizeof
); // page pointers per one page
45 nothrow @trusted @nogc:
46 uint rc
; // refcounter
47 void** mHugeDir
; // 2nd level directory page (allocated if mHugeRefs > 0; points to 1st level directories), or 1st level directory page (if mHugeRefs == 0)
48 uint mHugeRefs
; // number of refs in hugedir page
49 uint mAllocPages
; // number of allocated data pages
50 uint xcount
; // we'll keep it here too, so range iterators can use it
53 void opAssign() (in auto ref typeof(this)) { static assert(0, `assigning disabled`); }
55 @property uint allocedPages () const pure { pragma(inline
, true); return mAllocPages
; }
57 // so i can easily override it
58 void freePage(void* ptr
) {
61 import core
.stdc
.stdlib
: free
;
66 // so i can easily override it
67 void* mallocPage(bool doClear
) () {
68 import core
.stdc
.stdlib
: malloc
;
70 if (auto res
= malloc(PageSize
)) {
71 import core
.stdc
.string
: memset
;
72 memset(res
, 0, PageSize
);
79 return malloc(PageSize
);
83 // allocate one new page
84 // allocate new data page is `newData` is `null`
85 bool allocNewPage(bool requireMem
) (void* newData
=null) {
86 if (mHugeRefs
== 0 && mAllocPages
== RefsPerPage
) {
87 // we need to create hugedir
88 debug(debug_ssarray
) {{ import core
.stdc
.stdio
; printf("%p: creating hugedir (%p)\n", &this, mHugeDir
); }}
89 auto vv
= cast(void**)mallocPage
!true;
90 static if (!requireMem
) { if (vv
is null) return false; } else
91 if (vv
is null) assert(0, "PageStore: out of memory");
92 assert(mHugeDir
!is null);
93 vv
[0] = mHugeDir
; // first 1st level page
97 // allocate new data page (we'll need it anyway)
98 auto dp
= (newData
is null ? mallocPage
!false : newData
); // don't clear
99 static if (!requireMem
) { if (dp
is null) return false; } else
100 if (dp
is null) assert(0, "PageStore: out of memory");
102 if (mHugeRefs
== 0) {
103 if (mAllocPages
== 0 && mHugeDir
is null) {
104 // no dir page, allocate one
105 debug(debug_ssarray
) {{ import core
.stdc
.stdio
; printf("%p: creating smalldir\n", &this); }}
106 void** hd
= cast(void**)mallocPage
!true;
107 static if (!requireMem
) { if (hd
is null) { if (dp
!is newData
) freePage(dp
); return false; } } else
108 if (hd
is null) assert(0, "PageStore: out of memory");
111 assert(mAllocPages
+1 <= RefsPerPage
);
112 mHugeDir
[mAllocPages
++] = dp
;
114 // allocate new 1st level directory page if necessary
117 if (mAllocPages
%RefsPerPage
== 0) {
118 debug(debug_ssarray
) {{ import core
.stdc
.stdio
; printf("%p: creating new 1st-level dir page (ap=%u; hr=%u)\n", &this, mAllocPages
, mHugeRefs
); }}
119 // yep, last 1st level page is full, allocate new one
120 if (mHugeRefs
== RefsPerPage
) assert(0, "PageStore: out of directory space");
121 dirpg
= cast(void**)mallocPage
!true;
122 static if (!requireMem
) { if (dirpg
is null) { if (dp
!is newData
) freePage(dp
); return false; } } else
123 if (dirpg
is null) assert(0, "PageStore: out of memory");
124 mHugeDir
[mHugeRefs
++] = dirpg
;
125 debug(debug_ssarray
) {{ import core
.stdc
.stdio
; printf("%p: new huge item! pa=%u; hr=%u; ptr=%p\n", &this, mAllocPages
, mHugeRefs
, dirpg
); }}
127 // there should be some room in last 1st level page
128 assert(mHugeRefs
> 0);
129 dirpg
= cast(void**)mHugeDir
[mHugeRefs
-1];
130 dirpgeidx
= mAllocPages
%RefsPerPage
;
131 assert(dirpg
[dirpgeidx
] is null);
133 dirpg
[dirpgeidx
] = dp
;
139 void freeLastPage () {
140 if (mAllocPages
== 0) return;
141 --mAllocPages
; // little optimization: avoid `-1` everywhere
142 if (mHugeRefs
== 0) {
144 // free last data page
145 freePage(mHugeDir
[mAllocPages
]);
146 mHugeDir
[mAllocPages
] = null; // why not?
147 if (mAllocPages
== 0) {
148 // free catalog page too
149 debug(debug_ssarray
) {{ import core
.stdc
.stdio
; printf("%p: freeing smalldir\n", &this); }}
155 assert(mAllocPages
!= 0);
156 immutable uint lv1pgidx
= mAllocPages
/RefsPerPage
;
157 immutable uint dtpgidx
= mAllocPages
%RefsPerPage
;
158 // free last data page
159 void** pp
= cast(void**)mHugeDir
[lv1pgidx
];
160 freePage(pp
[dtpgidx
]);
161 pp
[dtpgidx
] = null; // required
163 debug(debug_ssarray
) {{ import core
.stdc
.stdio
; printf("%p: freeing last 1st-level dir page (ap=%u)\n", &this, mAllocPages
); }}
164 // we should free this catalog page too
167 // convert to one-level?
168 if (mAllocPages
== RefsPerPage
) {
169 assert(mHugeRefs
== 1);
170 debug(debug_ssarray
) {{ import core
.stdc
.stdio
; printf("%p: converting to smalldir\n", &this); }}
171 pp
= cast(void**)mHugeDir
[0];
172 // drop huge catalog page
181 // ensure that we have at least this number of bytes
182 void ensureSize(bool requireMem
) (uint size
) {
183 if (size
>= uint.max
/2) assert(0, "PageStore: out of memory"); // 2GB is enough for everyone!
184 while (size
> mAllocPages
*PageSize
) {
185 if (!allocNewPage
!requireMem()) {
186 static if (!requireMem
) break; else assert(0, "PageStore: out of memory");
191 // ensure that we have at least this number of pages
192 void ensurePages(bool requireMem
) (uint pgcount
) {
193 if (pgcount
>= uint.max
/2/PageSize
) assert(0, "PageStore: out of memory"); // 2GB is enough for everyone!
194 while (pgcount
> mAllocPages
) {
195 if (!allocNewPage
!requireMem()) {
196 static if (!requireMem
) break; else assert(0, "PageStore: out of memory");
203 import core
.stdc
.string
: memset
;
204 if (mHugeRefs
== 0) {
205 foreach (void* pg1
; mHugeDir
[0..mAllocPages
]) freePage(pg1
);
207 // for each 1st level dir page
208 foreach (void* pg1
; mHugeDir
[0..mHugeRefs
]) {
209 // for each page in 1st level dir page
210 foreach (void* dpg
; (cast(void**)pg1
)[0..RefsPerPage
]) freePage(dpg
);
215 memset(&this, 0, this.sizeof
);
218 uint lengthInFullPages () const pure { pragma(inline
, true); return (xcount
+ItemsPerPage
-1)/ItemsPerPage
; }
220 // get pointer to the first byte of the page with the given index
221 inout(ubyte)* pagePtr (uint pgidx
) inout pure {
222 pragma(inline
, true);
224 pgidx
< mAllocPages ?
226 cast(inout(ubyte)*)mHugeDir
[pgidx
] :
227 cast(inout(ubyte)*)(cast(void**)mHugeDir
[pgidx
/RefsPerPage
])[pgidx
%RefsPerPage
]) :
231 // get pointer to the directory entry for the given page
232 // used to move pages around
233 inout(void)** pageDirPtr (uint pgidx
) inout pure {
234 pragma(inline
, true);
236 pgidx
< mAllocPages ?
238 cast(inout(void)**)&mHugeDir
[pgidx
] :
239 cast(inout(void)**)&(cast(void**)mHugeDir
[pgidx
/RefsPerPage
])[pgidx
%RefsPerPage
]) :
247 @property inout(PageStore
)* psp () inout pure { pragma(inline
, true); return cast(inout(PageStore
*))psptr
; }
249 // ugly hack, but it never returns anyway
250 static void boundsError (uint idx
, uint len
) pure {
251 import std
.traits
: functionAttributes
, FunctionAttribute
, functionLinkage
, SetFunctionAttributes
, isFunctionPointer
, isDelegate
;
252 static auto assumePure(T
) (scope T t
) if (isFunctionPointer
!T || isDelegate
!T
) {
253 enum attrs
= functionAttributes
!T|FunctionAttribute
.pure_
;
254 return cast(SetFunctionAttributes
!(T
, functionLinkage
!T
, attrs
))t
;
257 import core
.stdc
.stdlib
: malloc
;
258 import core
.stdc
.stdio
: snprintf
;
259 char* msg
= cast(char*)malloc(1024);
260 auto len
= snprintf(msg
, 1024, "SSArray: out of bounds access; index=%u; length=%u", idx
, len
);
261 assert(0, msg
[0..len
]);
265 uint lengthInFullPages () const pure {
266 pragma(inline
, true);
267 return (psptr ?
((cast(PageStore
*)psptr
).xcount
+ItemsPerPage
-1)/ItemsPerPage
: 0);
270 static uint lengthInFullPages (uint count
) pure {
271 pragma(inline
, true);
272 return (count
+ItemsPerPage
-1)/ItemsPerPage
;
276 this (this) pure { pragma(inline
, true); if (psptr
) ++(cast(PageStore
*)psptr
).rc
; }
279 pragma(inline
, true);
281 if (--(cast(PageStore
*)psptr
).rc
== 0) (cast(PageStore
*)psptr
).clear();
285 void opAssign() (in auto ref Me src
) pure {
286 pragma(inline
, true);
287 if (src
.psptr
) ++(cast(PageStore
*)src
.psptr
).rc
;
289 if (--(cast(PageStore
*)psptr
).rc
== 0) (cast(PageStore
*)psptr
).clear();
294 /// remove all elements, free all memory
296 pragma(inline
, true);
298 if (--(cast(PageStore
*)psptr
).rc
== 0) (cast(PageStore
*)psptr
).clear();
302 ref inout(T
) currWrap (uint idx
) inout pure { pragma(inline
, true); if (length
== 0) assert(0, "SSArray: bounds error"); return this[idx
%length
]; } /// does wraparound
303 ref inout(T
) prevWrap (uint idx
) inout pure { pragma(inline
, true); if (length
== 0) assert(0, "SSArray: bounds error"); return this[(idx
+length
-1)%length
]; } /// does wraparound
304 ref inout(T
) nextWrap (uint idx
) inout pure { pragma(inline
, true); if (length
== 0) assert(0, "SSArray: bounds error"); return this[(idx
+1)%length
]; } /// does wraparound
306 /// returns number of elements in the array
307 @property uint length () const pure { pragma(inline
, true); return (psptr ?
(cast(PageStore
*)psptr
).xcount
: 0); }
308 /// sets new number of elements in the array (will free memory on shrinking)
309 @property void length (usize count
) {
310 pragma(inline
, true);
311 static if (usize
.sizeof
== 4) {
314 if (count
> uint.max
/2) assert(0, "SSArray: out of memory");
315 setLength(cast(uint)count
);
318 alias opDollar
= length
;
321 ref inout(T
) opIndex (uint idx
) inout pure {
322 pragma(inline
, true);
323 if (idx
>= length
) boundsError(idx
, length
);
324 return *((cast(inout(T
)*)((cast(PageStore
*)psptr
).pagePtr(idx
/ItemsPerPage
)))+idx
%ItemsPerPage
);
327 private bool ensurePS(bool requireMem
) () {
330 import core
.stdc
.stdlib
: malloc
;
331 import core
.stdc
.string
: memset
;
332 auto psx
= cast(PageStore
*)malloc(PageStore
.sizeof
);
333 static if (!requireMem
) { if (psx
is null) return false; } else
334 if (psx
is null) assert(0, "SSArray: out of memory");
335 memset(psx
, 0, PageStore
.sizeof
);
337 psptr
= cast(usize
)psx
;
342 /// reserve memory for the given number of elements (fail hard if `requireMem` is `true`)
343 void reserve(bool requireMem
=false) (uint count
) {
344 if (count
== 0) return;
345 if (uint.max
/2/T
.sizeof
< count
) {
346 static if (requireMem
) assert(0, "SSArray: out of memory");
347 else count
= uint.max
/2/T
.sizeof
;
349 if (!ensurePS
!requireMem()) return; // this will fail if necessary
350 psp
.ensureSize
!requireMem(lengthInFullPages(cast(uint)(T
.sizeof
*count
))*PageSize
);
353 /// set new array length.
354 /// if `doShrinkFree` is `true`, free memory on shrinking.
355 /// if `doClear` is `true`, fill new elements with `.init` on grow.
356 /// reserve memory for the given number of elements (fail hard if `requireMem` is `true`)
357 void setLength(bool doShrinkFree
=false, bool doClear
=initNewElements
) (uint count
) {
358 if (uint.max
/2/T
.sizeof
< count
) assert(0, "SSArray: out of memory");
359 if (count
== length
) return;
360 if (count
== 0) { if (psptr
) (cast(PageStore
*)psptr
).clear(); return; }
361 uint newPageCount
= lengthInFullPages(count
);
362 assert(newPageCount
> 0);
364 if (count
< (cast(PageStore
*)psptr
).xcount
) {
366 static if (doShrinkFree
) {
367 while ((cast(PageStore
*)psptr
).allocedPages
> newPageCount
) (cast(PageStore
*)psptr
).freeLastPage();
369 (cast(PageStore
*)psptr
).xcount
= count
;
372 debug(debug_ssarray
) if (psptr
) { import core
.stdc
.stdio
; printf("%p: grow000: ap=%u; sz=%u; qsz=%u; itperpage=%u; count=%u; length=%u\n", &this, (cast(PageStore
*)psptr
).allocedPages
, (cast(PageStore
*)psptr
).allocedPages
*PageSize
, count
*T
.sizeof
, ItemsPerPage
, count
, length
); }
373 (cast(PageStore
*)psptr
).ensurePages
!true(newPageCount
);
374 static if (doClear
) {
375 static if (__traits(isIntegral
, T
) && T
.init
== 0) {
377 static immutable it
= T
.init
;
378 static bool checked
= false;
379 static bool isZero
= false;
382 foreach (immutable ubyte v
; (cast(immutable(ubyte)*)(&it
))[0..it
.sizeof
]) b |
= v
;
387 // fill up previous last page
388 if ((cast(PageStore
*)psptr
).xcount
%ItemsPerPage
!= 0) {
389 uint itemsLeft
= ItemsPerPage
-(cast(PageStore
*)psptr
).xcount
%ItemsPerPage
;
390 if (itemsLeft
> count
-(cast(PageStore
*)psptr
).xcount
) itemsLeft
= count
-(cast(PageStore
*)psptr
).xcount
;
391 auto cp
= (cast(T
*)((cast(PageStore
*)psptr
).pagePtr((cast(PageStore
*)psptr
).xcount
/ItemsPerPage
)))+(cast(PageStore
*)psptr
).xcount
%ItemsPerPage
;
392 (cast(PageStore
*)psptr
).xcount
+= itemsLeft
;
393 static if (__traits(isIntegral
, T
) && T
.init
== 0) {
394 import core
.stdc
.string
: memset
;
395 //pragma(msg, "ZEROING! (000)");
396 memset(cp
, 0, itemsLeft
*T
.sizeof
);
399 import core
.stdc
.string
: memset
;
400 debug(debug_ssarray
) {{ import core
.stdc
.stdio
; printf("%p: ZERO FILL(000)\n", psp
); }}
401 memset(cp
, 0, itemsLeft
*T
.sizeof
);
403 import core
.stdc
.string
: memcpy
;
404 while (itemsLeft
--) {
405 memcpy(cp
, &it
, it
.sizeof
);
410 if (count
== (cast(PageStore
*)psptr
).xcount
) return;
413 assert((cast(PageStore
*)psptr
).xcount
%ItemsPerPage
== 0);
414 while ((cast(PageStore
*)psptr
).xcount
< count
) {
415 uint ileft
= count
-(cast(PageStore
*)psptr
).xcount
;
416 if (ileft
> ItemsPerPage
) ileft
= ItemsPerPage
;
417 auto cp
= cast(T
*)((cast(PageStore
*)psptr
).pagePtr((cast(PageStore
*)psptr
).xcount
/ItemsPerPage
));
418 //debug(debug_ssarray) {{ import core.stdc.stdio; printf("%p: xcount=%u; cp=%p; ileft=%u\n", psp, xcount, cp, ileft); }}
419 (cast(PageStore
*)psptr
).xcount
+= ileft
;
420 static if (__traits(isIntegral
, T
) && T
.init
== 0) {
421 import core
.stdc
.string
: memset
;
422 //pragma(msg, "ZEROING! (001)");
423 memset(cp
, 0, ileft
*T
.sizeof
);
426 import core
.stdc
.string
: memset
;
427 debug(debug_ssarray
) {{ import core
.stdc
.stdio
; printf("%p: ZERO FILL(001)\n", psp
); }}
428 memset(cp
, 0, ileft
*T
.sizeof
);
430 import core
.stdc
.string
: memcpy
;
432 memcpy(cp
, &it
, it
.sizeof
);
439 (cast(PageStore
*)psptr
).xcount
= count
;
444 /// remove `size` last elements, but don't free memory.
445 /// won't fail if `size` > `length`.
446 void chop (uint size
) {
447 pragma(inline
, true);
450 (cast(PageStore
*)psptr
).xcount
= 0;
452 (cast(PageStore
*)psptr
).xcount
-= size
;
457 /// remove all array elements, but don't free any memory.
458 void chopAll () { pragma(inline
, true); if (psptr
) (cast(PageStore
*)psptr
).xcount
= 0; }
460 /// append new element to the array. uses `memcpy()` to copy data.
461 void append() (in auto ref T t
) {
462 import core
.stdc
.string
: memcpy
;
463 setLength
!(false, false)(length
+1); // don't clear, don't shrink
464 memcpy(&this[$-1], &t
, T
.sizeof
);
467 /// append new element to the array. uses `memcpy()` to copy data.
468 void opOpAssign(string op
:"~") (in auto ref T t
) { pragma(inline
, true); append(t
); }
471 private import std
.traits
: ParameterTypeTuple
;
473 int opApply(DG
) (scope DG dg
) if (ParameterTypeTuple
!DG
.length
== 1 || ParameterTypeTuple
!DG
.length
== 2) {
474 // don't use `foreach` here, we *really* want to re-check length on each iteration
475 for (uint idx
= 0; idx
< (cast(PageStore
*)psptr
).xcount
; ++idx
) {
476 static if (ParameterTypeTuple
!DG
.length
== 1) {
478 if (auto res
= dg(this[idx
])) return res
;
482 if (auto res
= dg(xidx
, this[idx
])) return res
;
488 int opApplyReverse(DG
) (scope DG dg
) if (ParameterTypeTuple
!DG
.length
== 1 || ParameterTypeTuple
!DG
.length
== 2) {
489 foreach_reverse (immutable uint idx
; 0..length
) {
490 if (idx
>= length
) return 0; // yeah, recheck it
491 static if (ParameterTypeTuple
!DG
.length
== 1) {
493 if (auto res
= dg(this[idx
])) return res
;
497 if (auto res
= dg(xidx
, this[idx
])) return res
;
504 static struct Range
{
505 public nothrow @trusted @nogc:
513 this (uint a
, uint b
, uint c
) pure { pragma(inline
, true); psptr
= a
; pos
= b
; xend
= c
; if (psptr
) ++(cast(PageStore
*)psptr
).rc
; }
516 this() (in auto ref Me arr
) pure { pragma(inline
, true); psptr
= arr
.psptr
; xend
= arr
.length
; if (psptr
) ++(cast(PageStore
*)psptr
).rc
; }
517 this (this) pure { if (psptr
) ++(cast(PageStore
*)psptr
).rc
; }
519 pragma(inline
, true);
521 if (--(cast(PageStore
*)psptr
).rc
== 0) (cast(PageStore
*)psptr
).clear();
524 void opAssign() (in auto ref Range src
) pure {
525 pragma(inline
, true);
526 if (src
.psptr
) ++(cast(PageStore
*)src
.psptr
).rc
;
528 if (--(cast(PageStore
*)psptr
).rc
== 0) (cast(PageStore
*)psptr
).clear();
534 Range
save () const { pragma(inline
, true); return (empty ? Range
.init
: Range(psptr
, pos
, xend
)); }
535 @property bool empty () const pure { pragma(inline
, true); return (psptr
== 0 || pos
>= xend || pos
>= (cast(PageStore
*)psptr
).xcount
); }
536 @property ref inout(T
) front () inout pure {
537 version(aliced
) pragma(inline
, true);
538 if (psptr
&& pos
< xend
&& pos
< (cast(PageStore
*)psptr
).xcount
) {
539 return *((cast(inout(T
)*)((cast(PageStore
*)psptr
).pagePtr(pos
/ItemsPerPage
)))+pos
%ItemsPerPage
);
541 boundsError(pos
, length
);
542 assert(0); // make compiler happy
545 void popFront () pure { pragma(inline
, true); if (!empty
) ++pos
; }
546 uint length () const pure { pragma(inline
, true); return (empty ?
0 : (xend
< (cast(PageStore
*)psptr
).xcount ? xend
: (cast(PageStore
*)psptr
).xcount
)-pos
); }
547 alias opDollar
= length
;
549 Range
opSlice () const { version(aliced
) pragma(inline
, true); return (empty ? Range
.init
: Range(psptr
, pos
, xend
)); }
550 Range
opSlice (uint lo
, uint hi
) const {
551 version(aliced
) pragma(inline
, true);
552 if (lo
> length
) boundsError(lo
, length
);
553 if (hi
> length
) boundsError(hi
, length
);
554 if (lo
>= hi
) return Range
.init
;
555 return Range(psptr
, pos
+lo
, pos
+hi
);
558 ref inout(T
) opIndex (uint idx
) inout pure {
559 version(aliced
) pragma(inline
, true);
560 if (psptr
&& idx
>= 0 && idx
< length
) {
561 return *((cast(inout(T
)*)((cast(PageStore
*)psptr
).pagePtr((pos
+idx
)/ItemsPerPage
)))+(pos
+idx
)%ItemsPerPage
);
563 boundsError(idx
, length
);
564 assert(0); // make compiler happy
570 Range
opSlice () const { version(aliced
) pragma(inline
, true); return Range(this); }
573 Range
opSlice (uint lo
, uint hi
) const {
574 version(aliced
) pragma(inline
, true);
575 if (lo
> length
) boundsError(lo
, length
);
576 if (hi
> length
) boundsError(hi
, length
);
577 if (lo
>= hi
) return Range
.init
;
578 return Range(psptr
, lo
, hi
);
581 // ////////////////////////////////////////////////////////////////////// //
582 // low-level thingy, which may be handy sometimes
584 /// get direct pointer to the page data. pointer should not outlive the array.
585 /// $(WARNING shrinking array can invalidate pointer (but not enlarging).)
586 T
* pageDataPtr (uint pgidx
) const pure { pragma(inline
, true); return (psptr ?
cast(T
*)((cast(PageStore
*)psptr
).pagePtr(pgidx
)) : null); }
588 /// get total number of allocated pages
589 uint allocatedPages () const pure { pragma(inline
, true); return (psptr ?
(cast(PageStore
*)psptr
).mAllocPages
: 0); }
591 /// get number of used pages (there can be more allocated pages in the array)
592 uint usedPages () const pure { pragma(inline
, true); return (psptr ?
(cast(PageStore
*)psptr
).lengthInFullPages
: 0); }
594 /// insert new page before the given page; returns success flag
595 /// `beforePageIdx` can be one past the last page, but not more
596 /// this won't clear inserted elements with any value
597 /// if `requireMem` is `false`, rollback on OOM
598 /// if `data` is not null, don't allocate new data page, but use the given one
599 bool insertPageBefore(bool requireMem
=true) (uint beforePageIdx
, void* data
=null) {
600 PageStore
* ps
= cast(PageStore
*)psptr
;
602 if (beforePageIdx
> ps
.lengthInFullPages
) return false; // oops
604 if (ps
.lengthInFullPages
>= uint.max
/2/PageSize
) {
606 static if (requireMem
) assert(0, "SSArray: out of memory");
610 if (beforePageIdx
> 0) return false; // oops
611 if (!ensurePS
!requireMem()) return false; // this will fail if necessary
612 ps
= cast(PageStore
*)psptr
;
615 if (ps
.mAllocPages
< ps
.lengthInFullPages
+1) {
616 if (!ps
.allocNewPage
!requireMem(data
)) return false; // this will fail if necessary
619 if (beforePageIdx
!= ps
.lengthInFullPages
) {
620 // no, move page pointers
621 auto lpp
= *ps
.pageDirPtr(ps
.mAllocPages
-1);
622 foreach_reverse (immutable uint pidx
; beforePageIdx
+1..ps
.mAllocPages
) {
623 *ps
.pageDirPtr(pidx
) = *ps
.pageDirPtr(pidx
-1);
625 *ps
.pageDirPtr(beforePageIdx
) = lpp
;
628 ps
.xcount
= (ps
.lengthInFullPages
+1)*ItemsPerPage
;
632 /// remove the given page; returns success flag
633 /// if `doFreeMem` is `false`, don't free removed page, just move it into unused set
634 bool removePageAt(bool doFreeMem
=true) (uint pgidx
) {
635 PageStore
* ps
= cast(PageStore
*)psptr
;
636 if (ps
is null) return false; // can't
637 if (pgidx
>= ps
.lengthInFullPages
) return false; // can't
639 if (pgidx
!= ps
.lengthInFullPages
-1) {
640 // no, move page pointers
641 auto lpp
= *ps
.pageDirPtr(pgidx
);
642 foreach (immutable uint pidx
; pgidx
+1..ps
.mAllocPages
) {
643 *ps
.pageDirPtr(pidx
-1) = *ps
.pageDirPtr(pidx
);
645 *ps
.pageDirPtr(ps
.mAllocPages
-1) = lpp
;
647 static if (doFreeMem
) ps
.freeLastPage();
648 if (ps
.lengthInFullPages
== 1) {
651 uint newlen
= (ps
.lengthInFullPages
-1)*ItemsPerPage
;
652 if (ps
.xcount
> newlen
) ps
.xcount
= newlen
;
657 /// extract the given page; returns page data pointer of `null` on failure
658 void* extractPageAt (uint pgidx
) {
659 PageStore
* ps
= cast(PageStore
*)psptr
;
660 if (ps
is null) return null; // can't
661 if (pgidx
>= ps
.lengthInFullPages
) return null; // can't
662 void* res
= ps
.pagePtr(pgidx
);
664 if (pgidx
!= ps
.lengthInFullPages
-1) {
665 // no, move page pointers
666 auto lpp
= *ps
.pageDirPtr(pgidx
);
667 foreach (immutable uint pidx
; pgidx
+1..ps
.mAllocPages
) {
668 *ps
.pageDirPtr(pidx
-1) = *ps
.pageDirPtr(pidx
);
670 *ps
.pageDirPtr(ps
.mAllocPages
-1) = lpp
;
672 *ps
.pageDirPtr(ps
.mAllocPages
-1) = null; // page manager can live with this
674 if (ps
.lengthInFullPages
== 1) {
677 uint newlen
= (ps
.lengthInFullPages
-1)*ItemsPerPage
;
678 if (ps
.xcount
> newlen
) ps
.xcount
= newlen
;
685 version(test_ssarray
) unittest {
688 void testPostBlit (SSArray
!int ssa
) {
689 writefln("T000: ssa ptr/rc: %x/%u/%u", ssa
.psp
, ssa
.psp
.rc
, ssa
.length
);
690 foreach (uint idx
, int v
; ssa
) {
698 writefln("001: ssa ptr/rc: %x/%u/%u", ssa
.psp
, ssa
.psp
.rc
, ssa
.length
);
699 foreach (uint idx
, ref int v
; ssa
) {
705 ssa
.length
= ssa
.PageSize
/int.sizeof
+128;
706 writefln("002: ssa ptr/rc: %x/%u/%u; pages=%u", ssa
.psp
, ssa
.psp
.rc
, ssa
.length
, ssa
.psp
.allocedPages
);
708 ssa
.length
= (ssa
.PageSize
/int.sizeof
)*(ssa
.PageSize
/(void*).sizeof
)+4096;
709 writefln("003: ssa ptr/rc: %x/%u/%u; pages=%u", ssa
.psp
, ssa
.psp
.rc
, ssa
.length
, ssa
.psp
.allocedPages
);
711 ssa
.length
= ssa
.length
/2;
712 writefln("004: ssa ptr/rc: %x/%u/%u; pages=%u", ssa
.psp
, ssa
.psp
.rc
, ssa
.length
, ssa
.psp
.allocedPages
);
715 foreach (immutable uint v
; ssa
[2..6]) n
+= v
;
716 assert(n
== 2+3+4+5);
721 if (!ssa
.insertPageBefore(0)) assert(0, "wtf?!");
722 writefln("100: ssa ptr/rc/len/allocpg: %x/%u/%u/%u %x : %x : %x", ssa
.psp
, ssa
.psp
.rc
, ssa
.length
, ssa
.psp
.mAllocPages
, ssa
.pageDataPtr(0), ssa
.pageDataPtr(1), ssa
.pageDataPtr(2));
723 if (!ssa
.insertPageBefore(0)) assert(0, "wtf?!");
724 writefln("101: ssa ptr/rc/len/allocpg: %x/%u/%u/%u %x : %x : %x", ssa
.psp
, ssa
.psp
.rc
, ssa
.length
, ssa
.psp
.mAllocPages
, ssa
.pageDataPtr(0), ssa
.pageDataPtr(1), ssa
.pageDataPtr(2));
725 if (!ssa
.insertPageBefore(0)) assert(0, "wtf?!");
726 writefln("102: ssa ptr/rc/len/allocpg: %x/%u/%u/%u %x : %x : %x", ssa
.psp
, ssa
.psp
.rc
, ssa
.length
, ssa
.psp
.mAllocPages
, ssa
.pageDataPtr(0), ssa
.pageDataPtr(1), ssa
.pageDataPtr(2));
727 if (!ssa
.removePageAt(1)) assert(0, "wtf?!");
728 writefln("103: ssa ptr/rc/len/allocpg: %x/%u/%u/%u %x : %x : %x", ssa
.psp
, ssa
.psp
.rc
, ssa
.length
, ssa
.psp
.mAllocPages
, ssa
.pageDataPtr(0), ssa
.pageDataPtr(1), ssa
.pageDataPtr(2));
729 if (!ssa
.removePageAt(0)) assert(0, "wtf?!");
730 writefln("103: ssa ptr/rc/len/allocpg: %x/%u/%u/%u %x : %x : %x", ssa
.psp
, ssa
.psp
.rc
, ssa
.length
, ssa
.psp
.mAllocPages
, ssa
.pageDataPtr(0), ssa
.pageDataPtr(1), ssa
.pageDataPtr(2));
731 if (!ssa
.removePageAt(0)) assert(0, "wtf?!");
732 if (ssa
.psp
!is null) {
733 writefln("103: ssa ptr/rc/len/allocpg: %x/%u/%u/%u %x : %x : %x", ssa
.psp
, ssa
.psp
.rc
, ssa
.length
, ssa
.psp
.mAllocPages
, ssa
.pageDataPtr(0), ssa
.pageDataPtr(1), ssa
.pageDataPtr(2));
739 if (!ssa
.insertPageBefore(0)) assert(0, "wtf?!");
740 writefln("200: ssa ptr/rc/len/allocpg: %x/%u/%u/%u %x : %x : %x", ssa
.psp
, ssa
.psp
.rc
, ssa
.length
, ssa
.psp
.mAllocPages
, ssa
.pageDataPtr(0), ssa
.pageDataPtr(1), ssa
.pageDataPtr(2));
741 if (!ssa
.insertPageBefore(0)) assert(0, "wtf?!");
742 writefln("201: ssa ptr/rc/len/allocpg: %x/%u/%u/%u %x : %x : %x", ssa
.psp
, ssa
.psp
.rc
, ssa
.length
, ssa
.psp
.mAllocPages
, ssa
.pageDataPtr(0), ssa
.pageDataPtr(1), ssa
.pageDataPtr(2));
743 if (!ssa
.insertPageBefore(0)) assert(0, "wtf?!");
744 writefln("202: ssa ptr/rc/len/allocpg: %x/%u/%u/%u %x : %x : %x", ssa
.psp
, ssa
.psp
.rc
, ssa
.length
, ssa
.psp
.mAllocPages
, ssa
.pageDataPtr(0), ssa
.pageDataPtr(1), ssa
.pageDataPtr(2));
745 auto pg
= ssa
.extractPageAt(1);
746 if (pg
is null) assert(0, "wtf?!");
747 writefln("203: ssa ptr/rc/len/allocpg: %x/%u/%u/%u %x : %x : %x", ssa
.psp
, ssa
.psp
.rc
, ssa
.length
, ssa
.psp
.mAllocPages
, ssa
.pageDataPtr(0), ssa
.pageDataPtr(1), ssa
.pageDataPtr(2));
748 if (!ssa
.insertPageBefore(0, pg
)) assert(0, "wtf?!");
749 writefln("204: ssa ptr/rc/len/allocpg: %x/%u/%u/%u %x : %x : %x", ssa
.psp
, ssa
.psp
.rc
, ssa
.length
, ssa
.psp
.mAllocPages
, ssa
.pageDataPtr(0), ssa
.pageDataPtr(1), ssa
.pageDataPtr(2));