ba4e8abf2f852c92ccfbcb7b1965d26fc5a7adca
 src/gc/gcx.d |  374 ++++++++++++++++++++++++++++++++++++----------------------
 1 files changed, 231 insertions(+), 143 deletions(-)

diff --git a/src/gc/gcx.d b/src/gc/gcx.d
index d78e7bf..eaa7619 100644
--- a/src/gc/gcx.d
+++ b/src/gc/gcx.d
@@ -48,6 +48,16 @@ debug (PRINTF) import core.stdc.stdio : printf;
 debug (COLLECT_PRINTF) import core.stdc.stdio : printf;
 debug private import core.stdc.stdio;
 
+debug(PRINTF) void printFreeInfo(Pool* pool)
+{
+    uint nReallyFree;
+    foreach(i; 0..pool.npages) {
+        if(pool.pagetable[i] >= B_FREE) nReallyFree++;
+    }
+
+    printf("Pool %p:  %d really free, %d supposedly be free\n", pool, nReallyFree, pool.freepages);
+}
+
 private
 {
     enum USE_CACHE = true;
@@ -306,7 +316,7 @@ class GC
 
             if (pool)
             {
-                auto biti = cast(size_t)(p - pool.baseAddr) / 16;
+                auto biti = cast(size_t)(p - pool.baseAddr) / pool.divisor;
 
                 oldb = gcx.getBits(pool, biti);
             }
@@ -341,7 +351,7 @@ class GC
 
             if (pool)
             {
-                auto biti = cast(size_t)(p - pool.baseAddr) / 16;
+                auto biti = cast(size_t)(p - pool.baseAddr) / pool.divisor;
 
                 oldb = gcx.getBits(pool, biti);
                 gcx.setBits(pool, biti, mask);
@@ -377,7 +387,7 @@ class GC
 
             if (pool)
             {
-                auto biti = cast(size_t)(p - pool.baseAddr) / 16;
+                auto biti = cast(size_t)(p - pool.baseAddr) / pool.divisor;
 
                 oldb = gcx.getBits(pool, biti);
                 gcx.clrBits(pool, biti, mask);
@@ -467,14 +477,14 @@ class GC
                          * of full pages freed. Perhaps this should instead be the amount of
                          * memory freed.
                          */
-                        gcx.newPool(1);
+                        gcx.newPool(1,false);
                         state = 2;
                     }
                     else
                         state = 1;
                     continue;
                 case 1:
-                    gcx.newPool(1);
+                    gcx.newPool(1, false);
                     state = 2;
                     continue;
                 case 2:
@@ -511,7 +521,7 @@ class GC
             Pool *pool = gcx.findPool(p);
             assert(pool);
 
-            gcx.setBits(pool, cast(size_t)(p - pool.baseAddr) / 16, bits);
+            gcx.setBits(pool, cast(size_t)(p - pool.baseAddr) / pool.divisor, bits);
         }
         return p;
     }
@@ -602,7 +612,7 @@ class GC
 
                         if (pool)
                         {
-                            auto biti = cast(size_t)(p - pool.baseAddr) / 16;
+                            auto biti = cast(size_t)(p - pool.baseAddr) / pool.divisor;
 
                             if (bits)
                             {
@@ -642,6 +652,7 @@ class GC
                         {
                             debug (MEMSTOMP) memset(p + size, 0xF2, psize - size);
                             pool.freePages(pagenum + newsz, psz - newsz);
+                            pool.updateOffsets(pagenum);
                         }
                         if(alloc_size)
                             *alloc_size = newsz * PAGESIZE;
@@ -657,9 +668,13 @@ class GC
                                 if (i == pagenum + newsz)
                                 {
                                     debug (MEMSTOMP) memset(p + psize, 0xF0, size - psize);
+                                    debug(PRINTF) printFreeInfo(pool);
                                     memset(&pool.pagetable[pagenum + psz], B_PAGEPLUS, newsz - psz);
+                                    pool.updateOffsets(pagenum);
                                     if(alloc_size)
                                         *alloc_size = newsz * PAGESIZE;
+                                    pool.freepages -= (newsz - psz);
+                                    debug(PRINTF) printFreeInfo(pool);
                                     return p;
                                 }
                                 if (i == pool.ncommitted)
@@ -686,7 +701,7 @@ class GC
 
                         if (pool)
                         {
-                            auto biti = cast(size_t)(p - pool.baseAddr) / 16;
+                            auto biti = cast(size_t)(p - pool.baseAddr) / pool.divisor;
 
                             if (bits)
                             {
@@ -795,6 +810,8 @@ class GC
             return 0;
         debug (MEMSTOMP) memset(p + psize, 0xF0, (psz + sz) * PAGESIZE - psize);
         memset(pool.pagetable + pagenum + psz, B_PAGEPLUS, sz);
+        pool.updateOffsets(pagenum);
+        pool.freepages -= sz;
         if (p == gcx.cached_size_key)
             gcx.cached_size_val = (psz + sz) * PAGESIZE;
         if (p == gcx.cached_info_key)
@@ -862,6 +879,7 @@ class GC
     //
     private void freeNoSync(void *p)
     {
+        debug(PRINTF) printf("Freeing %p\n", cast(size_t) p);
         assert (p);
 
         Pool*  pool;
@@ -876,19 +894,19 @@ class GC
         sentinel_Invariant(p);
         p = sentinel_sub(p);
         pagenum = cast(size_t)(p - pool.baseAddr) / PAGESIZE;
-        biti = cast(size_t)(p - pool.baseAddr) / 16;
+
+        debug(PRINTF) printf("pool base = %p, PAGENUM = %d of %d / %d, bin = %d\n", pool.baseAddr, pagenum, pool.ncommitted, pool.npages, pool.pagetable[pagenum]);
+        debug(PRINTF) if(pool.isLargeObject) printf("Block size = %d\n", pool.bPageOffsets[pagenum]);
+        biti = cast(size_t)(p - pool.baseAddr) / pool.divisor;
+
         gcx.clrBits(pool, biti, BlkAttr.ALL_BITS);
 
         bin = cast(Bins)pool.pagetable[pagenum];
         if (bin == B_PAGE)              // if large alloc
         {   size_t npages;
-            size_t n;
 
             // Free pages
-            npages = 1;
-            n = pagenum;
-            while (++n < pool.ncommitted && pool.pagetable[n] == B_PAGEPLUS)
-                npages++;
+            npages = pool.bPageOffsets[pagenum];
             debug (MEMSTOMP) memset(p, 0xF2, npages * PAGESIZE);
             pool.freePages(pagenum, npages);
         }
@@ -1758,9 +1776,9 @@ struct Gcx
             }
             else if (bin == B_PAGEPLUS)
             {
-                do
-                {   --pn, offset -= PAGESIZE;
-                } while (cast(Bins)pool.pagetable[pn] == B_PAGEPLUS);
+                auto pageOffset = pool.bPageOffsets[pn];
+                offset -= pageOffset * PAGESIZE;
+                pn -= pageOffset;
 
                 return pool.baseAddr + (offset & (offset.max ^ (PAGESIZE-1)));
             }
@@ -1796,17 +1814,8 @@ struct Gcx
             bin = cast(Bins)pool.pagetable[pagenum];
             size = binsize[bin];
             if (bin == B_PAGE)
-            {   size_t npages = pool.ncommitted;
-                ubyte* pt;
-                size_t i;
-
-                pt = &pool.pagetable[0];
-                for (i = pagenum + 1; i < npages; i++)
-                {
-                    if (pt[i] != B_PAGEPLUS)
-                        break;
-                }
-                size = (i - pagenum) * PAGESIZE;
+            {
+                size = pool.bPageOffsets[pagenum] * PAGESIZE;
             }
             cached_size_key = p;
             cached_size_val = size;
@@ -1843,10 +1852,9 @@ struct Gcx
             }
             else if (bin == B_PAGEPLUS)
             {
-                do
-                {   --pn, offset -= PAGESIZE;
-                } while (cast(Bins)pool.pagetable[pn] == B_PAGEPLUS);
-
+                auto pageOffset = pool.bPageOffsets[pn];
+                offset = pageOffset * PAGESIZE;
+                pn -= pageOffset;
                 info.base = pool.baseAddr + (offset & (offset.max ^ (PAGESIZE-1)));
 
                 // fix bin for use by size calc below
@@ -1859,17 +1867,8 @@ struct Gcx
 
             info.size = binsize[bin];
             if (bin == B_PAGE)
-            {   size_t npages = pool.ncommitted;
-                ubyte* pt;
-                size_t i;
-
-                pt = &pool.pagetable[0];
-                for (i = pn + 1; i < npages; i++)
-                {
-                    if (pt[i] != B_PAGEPLUS)
-                        break;
-                }
-                info.size = (i - pn) * PAGESIZE;
+            {
+                info.size = pool.bPageOffsets[pn] * PAGESIZE;
             }
 
             ////////////////////////////////////////////////////////////////////
@@ -1879,7 +1878,7 @@ struct Gcx
             // reset the offset to the base pointer, otherwise the bits
             // are the bits for the pointer, which may be garbage
             offset = cast(size_t)(info.base - pool.baseAddr);
-            info.attr = getBits(pool, cast(size_t)(offset / 16));
+            info.attr = getBits(pool, cast(size_t)(offset / pool.divisor));
 
             cached_info_key = p;
             cached_info_val = info;
@@ -1943,7 +1942,9 @@ struct Gcx
     size_t reserve(size_t size)
     {
         size_t npages = (size + PAGESIZE - 1) / PAGESIZE;
-        Pool*  pool = newPool(npages);
+
+        // Assume reserve() is for small objects.
+        Pool*  pool = newPool(npages, false);
 
         if (!pool || pool.extendPages(npages) == OPFAIL)
             return 0;
@@ -1956,22 +1957,18 @@ struct Gcx
      */
     void minimize()
     {
+        debug(PRINTF) printf("Minimizing.\n");
         size_t n;
         size_t pn;
         Pool*  pool;
         size_t ncommitted;
 
+        Outer:
         for (n = 0; n < npools; n++)
         {
             pool = pooltable[n];
-            ncommitted = pool.ncommitted;
-            for (pn = 0; pn < ncommitted; pn++)
-            {
-                if (cast(Bins)pool.pagetable[pn] != B_FREE)
-                    break;
-            }
-            if (pn < ncommitted)
-                continue;
+            debug(PRINTF) printFreeInfo(pool);
+            if(pool.freepages < pool.npages) continue;
             pool.Dtor();
             cstdlib.free(pool);
             memmove(pooltable + n,
@@ -1981,6 +1978,7 @@ struct Gcx
         }
         minAddr = pooltable[0].baseAddr;
         maxAddr = pooltable[npools - 1].topAddr;
+        debug(PRINTF) printf("Done minimizing.\n");
     }
 
 
@@ -1990,6 +1988,8 @@ struct Gcx
      */
     void *bigAlloc(size_t size, size_t *alloc_size = null)
     {
+        debug(PRINTF) printf("In bigAlloc.  Size:  %d\n", size);
+
         Pool*  pool;
         size_t npages;
         size_t n;
@@ -2009,6 +2009,7 @@ struct Gcx
             for (n = 0; n < npools; n++)
             {
                 pool = pooltable[n];
+                if(!pool.isLargeObject || pool.freepages < npages) continue;
                 pn = pool.allocPages(npages);
                 if (pn != OPFAIL)
                     goto L1;
@@ -2028,7 +2029,7 @@ struct Gcx
                 // Release empty pools to prevent bloat
                 minimize();
                 // Allocate new pool
-                pool = newPool(npages);
+                pool = newPool(npages, true);
                 if (!pool)
                 {   state = 2;
                     continue;
@@ -2040,7 +2041,7 @@ struct Gcx
                 // Release empty pools to prevent bloat
                 minimize();
                 // Allocate new pool
-                pool = newPool(npages);
+                pool = newPool(npages, true);
                 if (!pool)
                 {
                     if (collected)
@@ -2059,10 +2060,17 @@ struct Gcx
         }
 
       L1:
+        debug(PRINTF) printFreeInfo(pool);
         pool.pagetable[pn] = B_PAGE;
         if (npages > 1)
             memset(&pool.pagetable[pn + 1], B_PAGEPLUS, npages - 1);
+        pool.updateOffsets(pn);
+        pool.freepages -= npages;
+
+        debug(PRINTF) printFreeInfo(pool);
+
         p = pool.baseAddr + pn * PAGESIZE;
+        debug(PRINTF) printf("Got large alloc:  %p, pt = %d, np = %d\n", p, pool.pagetable[pn], npages);
         memset(cast(char *)p + size, 0, npages * PAGESIZE - size);
         debug (MEMSTOMP) memset(p, 0xF1, size);
         if(alloc_size)
@@ -2080,7 +2088,7 @@ struct Gcx
      * Sort it into pooltable[].
      * Return null if failed.
      */
-    Pool *newPool(size_t npages)
+    Pool *newPool(size_t npages, bool isLargeObject)
     {
         Pool*  pool;
         Pool** newpooltable;
@@ -2121,7 +2129,7 @@ struct Gcx
         pool = cast(Pool *)cstdlib.calloc(1, Pool.sizeof);
         if (pool)
         {
-            pool.initialize(npages);
+            pool.initialize(npages, isLargeObject);
             if (!pool.baseAddr)
                 goto Lerr;
 
@@ -2171,6 +2179,7 @@ struct Gcx
         for (n = 0; n < npools; n++)
         {
             pool = pooltable[n];
+            if(pool.isLargeObject) continue;
             pn = pool.allocPages(1);
             if (pn != OPFAIL)
                 goto L1;
@@ -2179,6 +2188,7 @@ struct Gcx
 
       L1:
         pool.pagetable[pn] = cast(ubyte)bin;
+        pool.freepages--;
 
         // Convert page to free list
         size_t size = binsize[bin];
@@ -2229,22 +2239,20 @@ struct Gcx
                     // Adjust bit to be at start of allocated memory block
                     if (bin < B_PAGE)
                     {
-                        biti = (offset & notbinsize[bin]) >> 4;
+                        biti = (offset & notbinsize[bin]) / pool.divisor;
                         //debug(PRINTF) printf("\t\tbiti = x%x\n", biti);
                     }
                     else if (bin == B_PAGE)
                     {
-                        biti = (offset & notbinsize[bin]) >> 4;
+                        biti = (offset & notbinsize[bin]) / pool.divisor;
                         //debug(PRINTF) printf("\t\tbiti = x%x\n", biti);
 
                         pcache = cast(size_t)p & ~cast(size_t)(PAGESIZE-1);
                     }
                     else if (bin == B_PAGEPLUS)
                     {
-                        do
-                        {   --pn;
-                        } while (cast(Bins)pool.pagetable[pn] == B_PAGEPLUS);
-                        biti = pn * (PAGESIZE / 16);
+                        pn -= pool.bPageOffsets[pn];
+                        biti = pn * (PAGESIZE / pool.divisor);
 
                         pcache = cast(size_t)p & ~cast(size_t)(PAGESIZE-1);
                     }
@@ -2263,7 +2271,7 @@ struct Gcx
                             pool.scan.set(biti);
                             changes = 1;
                         }
-                        debug (LOGGING) log_parent(sentinel_add(pool.baseAddr + biti * 16), sentinel_add(pbot));
+                        debug (LOGGING) log_parent(sentinel_add(pool.baseAddr + biti * pool.divisor), sentinel_add(pbot));
                     }
                 }
             }
@@ -2392,6 +2400,8 @@ struct Gcx
             pool.freebits.zero();
         }
 
+        debug(COLLECT_PRINTF) printf("Set bits\n");
+
         // Mark each free entry, so it doesn't get scanned
         for (n = 0; n < B_PAGE; n++)
         {
@@ -2403,6 +2413,8 @@ struct Gcx
             }
         }
 
+        debug(COLLECT_PRINTF) printf("Marked free entries.\n");
+
         for (n = 0; n < npools; n++)
         {
             pool = pooltable[n];
@@ -2413,6 +2425,7 @@ struct Gcx
         {
             if (!noStack)
             {
+                debug(COLLECT_PRINTF) printf("scanning multithreaded stack.\n");
                 // Scan stacks and registers for each paused thread
                 thread_scanAll( &mark, stackTop );
             }
@@ -2465,18 +2478,18 @@ struct Gcx
                     }
                     *b = 0;
 
-                    auto o = pool.baseAddr + (b - bbase) * (typeof(bitm).sizeof*8) * 16;
+                    auto o = pool.baseAddr + (b - bbase) * (typeof(bitm).sizeof*8) * pool.divisor;
                     if (!(bitm & 0xFFFF))
                     {
                         bitm >>= 16;
-                        o += 16 * 16;
+                        o += 16 * pool.divisor;
                     }
                     if (!(bitm & 0xFF))
                     {
                         bitm >>= 8;
-                        o += 8 * 16;
+                        o += 8 * pool.divisor;
                     }
-                    for (; bitm; o += 16, bitm >>= 1)
+                    for (; bitm; o += pool.divisor, bitm >>= 1)
                     {
                         if (!(bitm & 1))
                             continue;
@@ -2491,12 +2504,9 @@ struct Gcx
                         {
                             if (bin == B_PAGEPLUS)
                             {
-                                while (pool.pagetable[pn - 1] != B_PAGE)
-                                    pn--;
+                                pn -= pool.bPageOffsets[pn];
                             }
-                            auto u = 1;
-                            while (pn + u < pool.ncommitted && pool.pagetable[pn + u] == B_PAGEPLUS)
-                                u++;
+                            auto u = pool.bPageOffsets[pn];
                             mark(o, o + u * PAGESIZE);
                         }
                     }
@@ -2517,69 +2527,14 @@ struct Gcx
             pool = pooltable[n];
             auto bbase = pool.mark.base();
             auto ncommitted = pool.ncommitted;
-            for (pn = 0; pn < ncommitted; pn++, bbase += PAGESIZE / (32 * 16))
-            {
-                Bins bin = cast(Bins)pool.pagetable[pn];
-
-                if (bin < B_PAGE)
-                {   byte* p;
-                    byte* ptop;
-                    size_t biti;
-                    size_t bitstride;
-                    auto   size = binsize[bin];
-
-                    p = pool.baseAddr + pn * PAGESIZE;
-                    ptop = p + PAGESIZE;
-                    biti = pn * (PAGESIZE/16);
-                    bitstride = size / 16;
-
-    version(none) // BUG: doesn't work because freebits() must also be cleared
-    {
-                    // If free'd entire page
-                    if (bbase[0] == 0 && bbase[1] == 0 && bbase[2] == 0 && bbase[3] == 0 &&
-                        bbase[4] == 0 && bbase[5] == 0 && bbase[6] == 0 && bbase[7] == 0)
-                    {
-                        for (; p < ptop; p += size, biti += bitstride)
-                        {
-                            if (pool.finals.nbits && pool.finals.testClear(biti))
-                                rt_finalize(cast(List *)sentinel_add(p), false/*noStack > 0*/);
-                            gcx.clrBits(pool, biti, BlkAttr.ALL_BITS);
-
-                            List *list = cast(List *)p;
-                            //debug(PRINTF) printf("\tcollecting %p\n", list);
-                            log_free(sentinel_add(list));
 
-                            debug (MEMSTOMP) memset(p, 0xF3, size);
-                        }
-                        pool.pagetable[pn] = B_FREE;
-                        freed += PAGESIZE;
-                        //debug(PRINTF) printf("freeing entire page %d\n", pn);
-                        continue;
-                    }
-    }
-                    for (; p < ptop; p += size, biti += bitstride)
-                    {
-                        if (!pool.mark.test(biti))
-                        {
-                            sentinel_Invariant(sentinel_add(p));
-
-                            pool.freebits.set(biti);
-                            if (pool.finals.nbits && pool.finals.testClear(biti))
-                                rt_finalize(cast(List *)sentinel_add(p), false/*noStack > 0*/);
-                            clrBits(pool, biti, BlkAttr.ALL_BITS);
-
-                            List *list = cast(List *)p;
-                            debug(PRINTF) printf("\tcollecting %p\n", list);
-                            log_free(sentinel_add(list));
-
-                            debug (MEMSTOMP) memset(p, 0xF3, size);
-
-                            freed += size;
-                        }
-                    }
-                }
-                else if (bin == B_PAGE)
-                {   size_t biti = pn * (PAGESIZE / 16);
+            if(pool.isLargeObject)
+            {
+                for(pn = 0; pn < ncommitted; pn++)
+                {
+                    Bins bin = cast(Bins)pool.pagetable[pn];
+                    if(bin > B_PAGE) continue;
+                    size_t biti = pn;
 
                     if (!pool.mark.test(biti))
                     {   byte *p = pool.baseAddr + pn * PAGESIZE;
@@ -2593,11 +2548,14 @@ struct Gcx
                         log_free(sentinel_add(p));
                         pool.pagetable[pn] = B_FREE;
                         freedpages++;
+                        pool.freepages++;
+
                         debug (MEMSTOMP) memset(p, 0xF3, PAGESIZE);
                         while (pn + 1 < ncommitted && pool.pagetable[pn + 1] == B_PAGEPLUS)
                         {
                             pn++;
                             pool.pagetable[pn] = B_FREE;
+                            pool.freepages++;
                             freedpages++;
 
                             debug (MEMSTOMP)
@@ -2607,6 +2565,75 @@ struct Gcx
                         }
                     }
                 }
+
+                continue;
+            }
+            else
+            {
+
+                for (pn = 0; pn < ncommitted; pn++, bbase += PAGESIZE / (32 * 16))
+                {
+                    Bins bin = cast(Bins)pool.pagetable[pn];
+
+                    if (bin < B_PAGE)
+                    {   byte* p;
+                        byte* ptop;
+                        size_t biti;
+                        size_t bitstride;
+                        auto   size = binsize[bin];
+
+                        p = pool.baseAddr + pn * PAGESIZE;
+                        ptop = p + PAGESIZE;
+                        biti = pn * (PAGESIZE/16);
+                        bitstride = size / 16;
+
+        version(none) // BUG: doesn't work because freebits() must also be cleared
+        {
+                        // If free'd entire page
+                        if (bbase[0] == 0 && bbase[1] == 0 && bbase[2] == 0 && bbase[3] == 0 &&
+                            bbase[4] == 0 && bbase[5] == 0 && bbase[6] == 0 && bbase[7] == 0)
+                        {
+                            for (; p < ptop; p += size, biti += bitstride)
+                            {
+                                if (pool.finals.nbits && pool.finals.testClear(biti))
+                                    rt_finalize(cast(List *)sentinel_add(p), false/*noStack > 0*/);
+                                gcx.clrBits(pool, biti, BlkAttr.ALL_BITS);
+
+                                List *list = cast(List *)p;
+                                //debug(PRINTF) printf("\tcollecting %p\n", list);
+                                log_free(sentinel_add(list));
+
+                                debug (MEMSTOMP) memset(p, 0xF3, size);
+                            }
+                            pool.pagetable[pn] = B_FREE;
+                            freed += PAGESIZE;
+                            pool.freepages++;
+                            //debug(PRINTF) printf("freeing entire page %d\n", pn);
+                            continue;
+                        }
+        }
+                        for (; p < ptop; p += size, biti += bitstride)
+                        {
+                            if (!pool.mark.test(biti))
+                            {
+                                sentinel_Invariant(sentinel_add(p));
+
+                                pool.freebits.set(biti);
+                                if (pool.finals.nbits && pool.finals.testClear(biti))
+                                    rt_finalize(cast(List *)sentinel_add(p), false/*noStack > 0*/);
+                                clrBits(pool, biti, BlkAttr.ALL_BITS);
+
+                                List *list = cast(List *)p;
+                                debug(PRINTF) printf("\tcollecting %p\n", list);
+                                log_free(sentinel_add(list));
+
+                                debug (MEMSTOMP) memset(p, 0xF3, size);
+
+                                freed += size;
+                            }
+                        }
+                    }
+                }
             }
         }
 
@@ -2621,6 +2648,7 @@ struct Gcx
             size_t ncommitted;
 
             pool = pooltable[n];
+            if(pool.isLargeObject) continue;
             ncommitted = pool.ncommitted;
             for (pn = 0; pn < ncommitted; pn++)
             {
@@ -2642,6 +2670,7 @@ struct Gcx
                             goto Lnotfree;
                     }
                     pool.pagetable[pn] = B_FREE;
+                    pool.freepages++;
                     recoveredpages++;
                     continue;
 
@@ -2689,7 +2718,7 @@ struct Gcx
             {
                 assert(p == cast(void*)((cast(size_t)p) & notbinsize[bins]));
                 // return true if the block is not marked.
-                return !(pool.mark.test(offset / 16));
+                return !(pool.mark.test(offset / pool.divisor));
             }
         }
         return false; // not collecting or pointer is a valid argument.
@@ -2916,12 +2945,21 @@ struct Pool
     GCBits appendable;  // entries that are appendable
 
     size_t npages;
+    size_t freepages;     // The number of pages not in use.
     size_t ncommitted;    // ncommitted <= npages
     ubyte* pagetable;
 
+    bool isLargeObject;
 
-    void initialize(size_t npages)
+    // This tracks how far back we have to go to find the nearest B_PAGE at
+    // a smaller address than a B_PAGEPLUS.  To save space, we use a uint.
+    // This limits individual allocations to 16 terabytes, assuming a 4k
+    // pagesize.
+    uint* bPageOffsets;
+
+    void initialize(size_t npages, bool isLargeObject)
     {
+        this.isLargeObject = isLargeObject;
         size_t poolsize;
 
         //debug(PRINTF) printf("Pool::Pool(%u)\n", npages);
@@ -2942,19 +2980,30 @@ struct Pool
         }
         //assert(baseAddr);
         topAddr = baseAddr + poolsize;
+        auto div = this.divisor;
+        auto nbits = cast(size_t)poolsize / div;
 
-        mark.alloc(cast(size_t)poolsize / 16);
-        scan.alloc(cast(size_t)poolsize / 16);
-        freebits.alloc(cast(size_t)poolsize / 16);
-        noscan.alloc(cast(size_t)poolsize / 16);
-        appendable.alloc(cast(size_t)poolsize / 16);
+        mark.alloc(nbits);
+        scan.alloc(nbits);
+        freebits.alloc(nbits);
+        noscan.alloc(nbits);
+        appendable.alloc(nbits);
 
         pagetable = cast(ubyte*)cstdlib.malloc(npages);
         if (!pagetable)
             onOutOfMemoryError();
+
+        if(isLargeObject)
+        {
+            bPageOffsets = cast(uint*)cstdlib.malloc(npages * uint.sizeof);
+            if (!bPageOffsets)
+                onOutOfMemoryError();
+        }
+
         memset(pagetable, B_UNCOMMITTED, npages);
 
         this.npages = npages;
+        this.freepages = npages;
         ncommitted = 0;
     }
 
@@ -2985,6 +3034,9 @@ struct Pool
         if (pagetable)
             cstdlib.free(pagetable);
 
+        if(bPageOffsets)
+            cstdlib.free(bPageOffsets);
+
         mark.Dtor();
         scan.Dtor();
         freebits.Dtor();
@@ -3021,6 +3073,24 @@ struct Pool
         }
     }
 
+    // The divisor used for determining bit indices.
+    size_t divisor() {
+        return isLargeObject ? PAGESIZE : 16;
+    }
+
+    void updateOffsets(size_t fromWhere)
+    {
+        assert(pagetable[fromWhere] == B_PAGE);
+        size_t pn = fromWhere + 1;
+        for(uint offset = 1; pn < ncommitted; pn++, offset++)
+        {
+            if(pagetable[pn] != B_PAGEPLUS) break;
+            bPageOffsets[pn] = offset;
+        }
+
+        // Store the size of the block in bPageOffsets[fromWhere].
+        bPageOffsets[fromWhere] = cast(uint) (pn - fromWhere);
+    }
 
     /**
      * Allocate n pages from Pool.
@@ -3028,6 +3098,7 @@ struct Pool
      */
     size_t allocPages(size_t n)
     {
+        if(freepages < n) return OPFAIL;
         size_t i;
         size_t n2;
 
@@ -3043,7 +3114,15 @@ struct Pool
                 }
             }
             else
+            {
                 n2 = n;
+                if(pagetable[i] == B_PAGE)
+                {
+                    // Then we have the offset information.  We can skip a
+                    // whole bunch of stuff.
+                    i += bPageOffsets[i] - 1;
+                }
+            }
         }
         return extendPages(n);
     }
@@ -3117,7 +3196,16 @@ struct Pool
      */
     void freePages(size_t pagenum, size_t npages)
     {
-        memset(&pagetable[pagenum], B_FREE, npages);
+        //memset(&pagetable[pagenum], B_FREE, npages);
+        for(size_t i = pagenum; i < npages + pagenum; i++)
+        {
+            if(pagetable[i] < B_FREE)
+            {
+                freepages++;
+            }
+
+            pagetable[i] = B_FREE;
+        }
     }
 
 
