Home | History | Annotate | Line # | Download | only in rt
      1      1.1  mrg /**
      2      1.1  mrg  * Implementation of associative arrays.
      3      1.1  mrg  *
      4      1.1  mrg  * Copyright: Copyright Digital Mars 2000 - 2015.
      5  1.1.1.2  mrg  * License:   $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
      6      1.1  mrg  * Authors:   Martin Nowak
      7  1.1.1.2  mrg  * Source: $(DRUNTIMESRC rt/_aaA.d)
      8      1.1  mrg  */
      9      1.1  mrg module rt.aaA;
     10      1.1  mrg 
     11      1.1  mrg /// AA version for debuggers, bump whenever changing the layout
     12      1.1  mrg extern (C) immutable int _aaVersion = 1;
     13      1.1  mrg 
     14      1.1  mrg import core.memory : GC;
     15  1.1.1.2  mrg import core.internal.util.math : min, max;
     16      1.1  mrg 
     17      1.1  mrg // grow threshold
     18      1.1  mrg private enum GROW_NUM = 4;
     19      1.1  mrg private enum GROW_DEN = 5;
     20      1.1  mrg // shrink threshold
     21      1.1  mrg private enum SHRINK_NUM = 1;
     22      1.1  mrg private enum SHRINK_DEN = 8;
     23      1.1  mrg // grow factor
     24      1.1  mrg private enum GROW_FAC = 4;
     25      1.1  mrg // growing the AA doubles it's size, so the shrink threshold must be
     26      1.1  mrg // smaller than half the grow threshold to have a hysteresis
     27      1.1  mrg static assert(GROW_FAC * SHRINK_NUM * GROW_DEN < GROW_NUM * SHRINK_DEN);
     28      1.1  mrg // initial load factor (for literals), mean of both thresholds
     29      1.1  mrg private enum INIT_NUM = (GROW_DEN * SHRINK_NUM + GROW_NUM * SHRINK_DEN) / 2;
     30      1.1  mrg private enum INIT_DEN = SHRINK_DEN * GROW_DEN;
     31      1.1  mrg 
     32      1.1  mrg private enum INIT_NUM_BUCKETS = 8;
     33      1.1  mrg // magic hash constants to distinguish empty, deleted, and filled buckets
     34      1.1  mrg private enum HASH_EMPTY = 0;
     35      1.1  mrg private enum HASH_DELETED = 0x1;
     36      1.1  mrg private enum HASH_FILLED_MARK = size_t(1) << 8 * size_t.sizeof - 1;
     37      1.1  mrg 
     38      1.1  mrg /// Opaque AA wrapper
     39      1.1  mrg struct AA
     40      1.1  mrg {
     41      1.1  mrg     Impl* impl;
     42      1.1  mrg     alias impl this;
     43      1.1  mrg 
     44      1.1  mrg     private @property bool empty() const pure nothrow @nogc
     45      1.1  mrg     {
     46      1.1  mrg         return impl is null || !impl.length;
     47      1.1  mrg     }
     48      1.1  mrg }
     49      1.1  mrg 
     50      1.1  mrg private struct Impl
     51      1.1  mrg {
     52      1.1  mrg private:
     53  1.1.1.2  mrg     this(scope const TypeInfo_AssociativeArray ti, size_t sz = INIT_NUM_BUCKETS)
     54      1.1  mrg     {
     55      1.1  mrg         keysz = cast(uint) ti.key.tsize;
     56      1.1  mrg         valsz = cast(uint) ti.value.tsize;
     57      1.1  mrg         buckets = allocBuckets(sz);
     58      1.1  mrg         firstUsed = cast(uint) buckets.length;
     59      1.1  mrg         valoff = cast(uint) talign(keysz, ti.value.talign);
     60      1.1  mrg 
     61      1.1  mrg         import rt.lifetime : hasPostblit, unqualify;
     62      1.1  mrg 
     63      1.1  mrg         if (hasPostblit(unqualify(ti.key)))
     64      1.1  mrg             flags |= Flags.keyHasPostblit;
     65      1.1  mrg         if ((ti.key.flags | ti.value.flags) & 1)
     66      1.1  mrg             flags |= Flags.hasPointers;
     67  1.1.1.2  mrg 
     68  1.1.1.2  mrg         entryTI = fakeEntryTI(this, ti.key, ti.value);
     69      1.1  mrg     }
     70      1.1  mrg 
     71      1.1  mrg     Bucket[] buckets;
     72      1.1  mrg     uint used;
     73      1.1  mrg     uint deleted;
     74      1.1  mrg     TypeInfo_Struct entryTI;
     75      1.1  mrg     uint firstUsed;
     76      1.1  mrg     immutable uint keysz;
     77      1.1  mrg     immutable uint valsz;
     78      1.1  mrg     immutable uint valoff;
     79      1.1  mrg     Flags flags;
     80      1.1  mrg 
     81      1.1  mrg     enum Flags : ubyte
     82      1.1  mrg     {
     83      1.1  mrg         none = 0x0,
     84      1.1  mrg         keyHasPostblit = 0x1,
     85      1.1  mrg         hasPointers = 0x2,
     86      1.1  mrg     }
     87      1.1  mrg 
     88      1.1  mrg     @property size_t length() const pure nothrow @nogc
     89      1.1  mrg     {
     90      1.1  mrg         assert(used >= deleted);
     91      1.1  mrg         return used - deleted;
     92      1.1  mrg     }
     93      1.1  mrg 
     94      1.1  mrg     @property size_t dim() const pure nothrow @nogc @safe
     95      1.1  mrg     {
     96      1.1  mrg         return buckets.length;
     97      1.1  mrg     }
     98      1.1  mrg 
     99      1.1  mrg     @property size_t mask() const pure nothrow @nogc
    100      1.1  mrg     {
    101      1.1  mrg         return dim - 1;
    102      1.1  mrg     }
    103      1.1  mrg 
    104      1.1  mrg     // find the first slot to insert a value with hash
    105      1.1  mrg     inout(Bucket)* findSlotInsert(size_t hash) inout pure nothrow @nogc
    106      1.1  mrg     {
    107      1.1  mrg         for (size_t i = hash & mask, j = 1;; ++j)
    108      1.1  mrg         {
    109      1.1  mrg             if (!buckets[i].filled)
    110      1.1  mrg                 return &buckets[i];
    111      1.1  mrg             i = (i + j) & mask;
    112      1.1  mrg         }
    113      1.1  mrg     }
    114      1.1  mrg 
    115      1.1  mrg     // lookup a key
    116  1.1.1.2  mrg     inout(Bucket)* findSlotLookup(size_t hash, scope const void* pkey, scope const TypeInfo keyti) inout
    117      1.1  mrg     {
    118      1.1  mrg         for (size_t i = hash & mask, j = 1;; ++j)
    119      1.1  mrg         {
    120      1.1  mrg             if (buckets[i].hash == hash && keyti.equals(pkey, buckets[i].entry))
    121      1.1  mrg                 return &buckets[i];
    122      1.1  mrg             else if (buckets[i].empty)
    123      1.1  mrg                 return null;
    124      1.1  mrg             i = (i + j) & mask;
    125      1.1  mrg         }
    126      1.1  mrg     }
    127      1.1  mrg 
    128  1.1.1.2  mrg     void grow(scope const TypeInfo keyti)
    129      1.1  mrg     {
    130      1.1  mrg         // If there are so many deleted entries, that growing would push us
    131      1.1  mrg         // below the shrink threshold, we just purge deleted entries instead.
    132      1.1  mrg         if (length * SHRINK_DEN < GROW_FAC * dim * SHRINK_NUM)
    133      1.1  mrg             resize(dim);
    134      1.1  mrg         else
    135      1.1  mrg             resize(GROW_FAC * dim);
    136      1.1  mrg     }
    137      1.1  mrg 
    138  1.1.1.2  mrg     void shrink(scope const TypeInfo keyti)
    139      1.1  mrg     {
    140      1.1  mrg         if (dim > INIT_NUM_BUCKETS)
    141      1.1  mrg             resize(dim / GROW_FAC);
    142      1.1  mrg     }
    143      1.1  mrg 
    144      1.1  mrg     void resize(size_t ndim) pure nothrow
    145      1.1  mrg     {
    146      1.1  mrg         auto obuckets = buckets;
    147      1.1  mrg         buckets = allocBuckets(ndim);
    148      1.1  mrg 
    149      1.1  mrg         foreach (ref b; obuckets[firstUsed .. $])
    150      1.1  mrg             if (b.filled)
    151      1.1  mrg                 *findSlotInsert(b.hash) = b;
    152      1.1  mrg 
    153      1.1  mrg         firstUsed = 0;
    154      1.1  mrg         used -= deleted;
    155      1.1  mrg         deleted = 0;
    156      1.1  mrg         GC.free(obuckets.ptr); // safe to free b/c impossible to reference
    157      1.1  mrg     }
    158      1.1  mrg 
    159      1.1  mrg     void clear() pure nothrow
    160      1.1  mrg     {
    161      1.1  mrg         import core.stdc.string : memset;
    162      1.1  mrg         // clear all data, but don't change bucket array length
    163      1.1  mrg         memset(&buckets[firstUsed], 0, (buckets.length - firstUsed) * Bucket.sizeof);
    164      1.1  mrg         deleted = used = 0;
    165      1.1  mrg         firstUsed = cast(uint) dim;
    166      1.1  mrg     }
    167      1.1  mrg }
    168      1.1  mrg 
    169      1.1  mrg //==============================================================================
    170      1.1  mrg // Bucket
    171      1.1  mrg //------------------------------------------------------------------------------
    172      1.1  mrg 
    173      1.1  mrg private struct Bucket
    174      1.1  mrg {
    175      1.1  mrg private pure nothrow @nogc:
    176      1.1  mrg     size_t hash;
    177      1.1  mrg     void* entry;
    178      1.1  mrg 
    179      1.1  mrg     @property bool empty() const
    180      1.1  mrg     {
    181      1.1  mrg         return hash == HASH_EMPTY;
    182      1.1  mrg     }
    183      1.1  mrg 
    184      1.1  mrg     @property bool deleted() const
    185      1.1  mrg     {
    186      1.1  mrg         return hash == HASH_DELETED;
    187      1.1  mrg     }
    188      1.1  mrg 
    189      1.1  mrg     @property bool filled() const @safe
    190      1.1  mrg     {
    191      1.1  mrg         return cast(ptrdiff_t) hash < 0;
    192      1.1  mrg     }
    193      1.1  mrg }
    194      1.1  mrg 
    195      1.1  mrg Bucket[] allocBuckets(size_t dim) @trusted pure nothrow
    196      1.1  mrg {
    197      1.1  mrg     enum attr = GC.BlkAttr.NO_INTERIOR;
    198      1.1  mrg     immutable sz = dim * Bucket.sizeof;
    199      1.1  mrg     return (cast(Bucket*) GC.calloc(sz, attr))[0 .. dim];
    200      1.1  mrg }
    201      1.1  mrg 
    202      1.1  mrg //==============================================================================
    203      1.1  mrg // Entry
    204      1.1  mrg //------------------------------------------------------------------------------
    205      1.1  mrg 
    206  1.1.1.2  mrg private void* allocEntry(scope const Impl* aa, scope const void* pkey)
    207      1.1  mrg {
    208      1.1  mrg     import rt.lifetime : _d_newitemU;
    209      1.1  mrg     import core.stdc.string : memcpy, memset;
    210      1.1  mrg 
    211      1.1  mrg     immutable akeysz = aa.valoff;
    212      1.1  mrg     void* res = void;
    213      1.1  mrg     if (aa.entryTI)
    214      1.1  mrg         res = _d_newitemU(aa.entryTI);
    215      1.1  mrg     else
    216      1.1  mrg     {
    217      1.1  mrg         auto flags = (aa.flags & Impl.Flags.hasPointers) ? 0 : GC.BlkAttr.NO_SCAN;
    218      1.1  mrg         res = GC.malloc(akeysz + aa.valsz, flags);
    219      1.1  mrg     }
    220      1.1  mrg 
    221      1.1  mrg     memcpy(res, pkey, aa.keysz); // copy key
    222      1.1  mrg     memset(res + akeysz, 0, aa.valsz); // zero value
    223      1.1  mrg 
    224      1.1  mrg     return res;
    225      1.1  mrg }
    226      1.1  mrg 
    227      1.1  mrg package void entryDtor(void* p, const TypeInfo_Struct sti)
    228      1.1  mrg {
    229      1.1  mrg     // key and value type info stored after the TypeInfo_Struct by tiEntry()
    230      1.1  mrg     auto sizeti = __traits(classInstanceSize, TypeInfo_Struct);
    231      1.1  mrg     auto extra = cast(const(TypeInfo)*)(cast(void*) sti + sizeti);
    232      1.1  mrg     extra[0].destroy(p);
    233      1.1  mrg     extra[1].destroy(p + talign(extra[0].tsize, extra[1].talign));
    234      1.1  mrg }
    235      1.1  mrg 
    236      1.1  mrg private bool hasDtor(const TypeInfo ti)
    237      1.1  mrg {
    238      1.1  mrg     import rt.lifetime : unqualify;
    239      1.1  mrg 
    240      1.1  mrg     if (typeid(ti) is typeid(TypeInfo_Struct))
    241      1.1  mrg         if ((cast(TypeInfo_Struct) cast(void*) ti).xdtor)
    242      1.1  mrg             return true;
    243      1.1  mrg     if (typeid(ti) is typeid(TypeInfo_StaticArray))
    244      1.1  mrg         return hasDtor(unqualify(ti.next));
    245      1.1  mrg 
    246      1.1  mrg     return false;
    247      1.1  mrg }
    248      1.1  mrg 
    249  1.1.1.2  mrg private immutable(void)* getRTInfo(const TypeInfo ti)
    250  1.1.1.2  mrg {
    251  1.1.1.2  mrg     // classes are references
    252  1.1.1.2  mrg     const isNoClass = ti && typeid(ti) !is typeid(TypeInfo_Class);
    253  1.1.1.2  mrg     return isNoClass ? ti.rtInfo() : rtinfoHasPointers;
    254  1.1.1.2  mrg }
    255  1.1.1.2  mrg 
    256      1.1  mrg // build type info for Entry with additional key and value fields
    257  1.1.1.2  mrg TypeInfo_Struct fakeEntryTI(ref Impl aa, const TypeInfo keyti, const TypeInfo valti)
    258      1.1  mrg {
    259      1.1  mrg     import rt.lifetime : unqualify;
    260      1.1  mrg 
    261      1.1  mrg     auto kti = unqualify(keyti);
    262      1.1  mrg     auto vti = unqualify(valti);
    263  1.1.1.2  mrg 
    264  1.1.1.2  mrg     // figure out whether RTInfo has to be generated (indicated by rtisize > 0)
    265  1.1.1.2  mrg     enum pointersPerWord = 8 * (void*).sizeof * (void*).sizeof;
    266  1.1.1.2  mrg     auto rtinfo = rtinfoNoPointers;
    267  1.1.1.2  mrg     size_t rtisize = 0;
    268  1.1.1.2  mrg     immutable(size_t)* keyinfo = void;
    269  1.1.1.2  mrg     immutable(size_t)* valinfo = void;
    270  1.1.1.2  mrg     if (aa.flags & Impl.Flags.hasPointers)
    271  1.1.1.2  mrg     {
    272  1.1.1.2  mrg         // classes are references
    273  1.1.1.2  mrg         keyinfo = cast(immutable(size_t)*) getRTInfo(keyti);
    274  1.1.1.2  mrg         valinfo = cast(immutable(size_t)*) getRTInfo(valti);
    275  1.1.1.2  mrg 
    276  1.1.1.2  mrg         if (keyinfo is rtinfoHasPointers && valinfo is rtinfoHasPointers)
    277  1.1.1.2  mrg             rtinfo = rtinfoHasPointers;
    278  1.1.1.2  mrg         else
    279  1.1.1.2  mrg             rtisize = 1 + (aa.valoff + aa.valsz + pointersPerWord - 1) / pointersPerWord;
    280  1.1.1.2  mrg     }
    281  1.1.1.2  mrg     bool entryHasDtor = hasDtor(kti) || hasDtor(vti);
    282  1.1.1.2  mrg     if (rtisize == 0 && !entryHasDtor)
    283      1.1  mrg         return null;
    284      1.1  mrg 
    285      1.1  mrg     // save kti and vti after type info for struct
    286      1.1  mrg     enum sizeti = __traits(classInstanceSize, TypeInfo_Struct);
    287  1.1.1.2  mrg     void* p = GC.malloc(sizeti + (2 + rtisize) * (void*).sizeof);
    288      1.1  mrg     import core.stdc.string : memcpy;
    289      1.1  mrg 
    290  1.1.1.2  mrg     memcpy(p, __traits(initSymbol, TypeInfo_Struct).ptr, sizeti);
    291      1.1  mrg 
    292      1.1  mrg     auto ti = cast(TypeInfo_Struct) p;
    293      1.1  mrg     auto extra = cast(TypeInfo*)(p + sizeti);
    294      1.1  mrg     extra[0] = cast() kti;
    295      1.1  mrg     extra[1] = cast() vti;
    296      1.1  mrg 
    297  1.1.1.2  mrg     static immutable tiMangledName = "S2rt3aaA__T5EntryZ";
    298  1.1.1.2  mrg     ti.mangledName = tiMangledName;
    299  1.1.1.2  mrg 
    300  1.1.1.2  mrg     ti.m_RTInfo = rtisize > 0 ? rtinfoEntry(aa, keyinfo, valinfo, cast(size_t*)(extra + 2), rtisize) : rtinfo;
    301  1.1.1.2  mrg     ti.m_flags = ti.m_RTInfo is rtinfoNoPointers ? cast(TypeInfo_Struct.StructFlags)0 : TypeInfo_Struct.StructFlags.hasPointers;
    302      1.1  mrg 
    303      1.1  mrg     // we don't expect the Entry objects to be used outside of this module, so we have control
    304      1.1  mrg     // over the non-usage of the callback methods and other entries and can keep these null
    305      1.1  mrg     // xtoHash, xopEquals, xopCmp, xtoString and xpostblit
    306  1.1.1.2  mrg     immutable entrySize = aa.valoff + aa.valsz;
    307      1.1  mrg     ti.m_init = (cast(ubyte*) null)[0 .. entrySize]; // init length, but not ptr
    308      1.1  mrg 
    309  1.1.1.2  mrg     if (entryHasDtor)
    310  1.1.1.2  mrg     {
    311  1.1.1.2  mrg         // xdtor needs to be built from the dtors of key and value for the GC
    312  1.1.1.2  mrg         ti.xdtorti = &entryDtor;
    313  1.1.1.2  mrg         ti.m_flags |= TypeInfo_Struct.StructFlags.isDynamicType;
    314  1.1.1.2  mrg     }
    315      1.1  mrg 
    316      1.1  mrg     ti.m_align = cast(uint) max(kti.talign, vti.talign);
    317      1.1  mrg 
    318      1.1  mrg     return ti;
    319      1.1  mrg }
    320      1.1  mrg 
    321  1.1.1.2  mrg // build appropriate RTInfo at runtime
    322  1.1.1.2  mrg immutable(void)* rtinfoEntry(ref Impl aa, immutable(size_t)* keyinfo, immutable(size_t)* valinfo, size_t* rtinfoData, size_t rtinfoSize)
    323  1.1.1.2  mrg {
    324  1.1.1.2  mrg     enum bitsPerWord = 8 * size_t.sizeof;
    325  1.1.1.2  mrg 
    326  1.1.1.2  mrg     rtinfoData[0] = aa.valoff + aa.valsz;
    327  1.1.1.2  mrg     rtinfoData[1..rtinfoSize] = 0;
    328  1.1.1.2  mrg 
    329  1.1.1.2  mrg     void copyKeyInfo(string src)()
    330  1.1.1.2  mrg     {
    331  1.1.1.2  mrg         size_t pos = 1;
    332  1.1.1.2  mrg         size_t keybits = aa.keysz / (void*).sizeof;
    333  1.1.1.2  mrg         while (keybits >= bitsPerWord)
    334  1.1.1.2  mrg         {
    335  1.1.1.2  mrg             rtinfoData[pos] = mixin(src);
    336  1.1.1.2  mrg             keybits -= bitsPerWord;
    337  1.1.1.2  mrg             pos++;
    338  1.1.1.2  mrg         }
    339  1.1.1.2  mrg         if (keybits > 0)
    340  1.1.1.2  mrg             rtinfoData[pos] = mixin(src) & ((cast(size_t) 1 << keybits) - 1);
    341  1.1.1.2  mrg     }
    342  1.1.1.2  mrg 
    343  1.1.1.2  mrg     if (keyinfo is rtinfoHasPointers)
    344  1.1.1.2  mrg         copyKeyInfo!"~cast(size_t) 0"();
    345  1.1.1.2  mrg     else if (keyinfo !is rtinfoNoPointers)
    346  1.1.1.2  mrg         copyKeyInfo!"keyinfo[pos]"();
    347  1.1.1.2  mrg 
    348  1.1.1.2  mrg     void copyValInfo(string src)()
    349  1.1.1.2  mrg     {
    350  1.1.1.2  mrg         size_t bitpos = aa.valoff / (void*).sizeof;
    351  1.1.1.2  mrg         size_t pos = 1;
    352  1.1.1.2  mrg         size_t dstpos = 1 + bitpos / bitsPerWord;
    353  1.1.1.2  mrg         size_t begoff = bitpos % bitsPerWord;
    354  1.1.1.2  mrg         size_t valbits = aa.valsz / (void*).sizeof;
    355  1.1.1.2  mrg         size_t endoff = (bitpos + valbits) % bitsPerWord;
    356  1.1.1.2  mrg         for (;;)
    357  1.1.1.2  mrg         {
    358  1.1.1.2  mrg             const bits = bitsPerWord - begoff;
    359  1.1.1.2  mrg             size_t s = mixin(src);
    360  1.1.1.2  mrg             rtinfoData[dstpos] |= s << begoff;
    361  1.1.1.2  mrg             if (begoff > 0 && valbits > bits)
    362  1.1.1.2  mrg                 rtinfoData[dstpos+1] |= s >> bits;
    363  1.1.1.2  mrg             if (valbits < bitsPerWord)
    364  1.1.1.2  mrg                 break;
    365  1.1.1.2  mrg             valbits -= bitsPerWord;
    366  1.1.1.2  mrg             dstpos++;
    367  1.1.1.2  mrg             pos++;
    368  1.1.1.2  mrg         }
    369  1.1.1.2  mrg         if (endoff > 0)
    370  1.1.1.2  mrg             rtinfoData[dstpos] &= ((cast(size_t) 1 << endoff) - 1);
    371  1.1.1.2  mrg     }
    372  1.1.1.2  mrg 
    373  1.1.1.2  mrg     if (valinfo is rtinfoHasPointers)
    374  1.1.1.2  mrg         copyValInfo!"~cast(size_t) 0"();
    375  1.1.1.2  mrg     else if (valinfo !is rtinfoNoPointers)
    376  1.1.1.2  mrg         copyValInfo!"valinfo[pos]"();
    377  1.1.1.2  mrg 
    378  1.1.1.2  mrg     return cast(immutable(void)*) rtinfoData;
    379  1.1.1.2  mrg }
    380  1.1.1.2  mrg 
    381  1.1.1.2  mrg unittest
    382  1.1.1.2  mrg {
    383  1.1.1.2  mrg     void test(K, V)()
    384  1.1.1.2  mrg     {
    385  1.1.1.2  mrg         static struct Entry
    386  1.1.1.2  mrg         {
    387  1.1.1.2  mrg             K key;
    388  1.1.1.2  mrg             V val;
    389  1.1.1.2  mrg         }
    390  1.1.1.2  mrg         auto keyti = typeid(K);
    391  1.1.1.2  mrg         auto valti = typeid(V);
    392  1.1.1.2  mrg         auto valrti = getRTInfo(valti);
    393  1.1.1.2  mrg         auto keyrti = getRTInfo(keyti);
    394  1.1.1.2  mrg 
    395  1.1.1.2  mrg         auto impl = new Impl(typeid(V[K]));
    396  1.1.1.2  mrg         if (valrti is rtinfoNoPointers && keyrti is rtinfoNoPointers)
    397  1.1.1.2  mrg         {
    398  1.1.1.2  mrg             assert(!(impl.flags & Impl.Flags.hasPointers));
    399  1.1.1.2  mrg             assert(impl.entryTI is null);
    400  1.1.1.2  mrg         }
    401  1.1.1.2  mrg         else if (valrti is rtinfoHasPointers && keyrti is rtinfoHasPointers)
    402  1.1.1.2  mrg         {
    403  1.1.1.2  mrg             assert(impl.flags & Impl.Flags.hasPointers);
    404  1.1.1.2  mrg             assert(impl.entryTI is null);
    405  1.1.1.2  mrg         }
    406  1.1.1.2  mrg         else
    407  1.1.1.2  mrg         {
    408  1.1.1.2  mrg             auto rtInfo  = cast(size_t*) impl.entryTI.rtInfo();
    409  1.1.1.2  mrg             auto refInfo = cast(size_t*) typeid(Entry).rtInfo();
    410  1.1.1.2  mrg             assert(rtInfo[0] == refInfo[0]); // size
    411  1.1.1.2  mrg             enum bytesPerWord = 8 * size_t.sizeof * (void*).sizeof;
    412  1.1.1.2  mrg             size_t words = (rtInfo[0] + bytesPerWord - 1) / bytesPerWord;
    413  1.1.1.2  mrg             foreach (i; 0 .. words)
    414  1.1.1.2  mrg                 assert(rtInfo[1 + i] == refInfo[i + 1]);
    415  1.1.1.2  mrg         }
    416  1.1.1.2  mrg     }
    417  1.1.1.2  mrg     test!(long, int)();
    418  1.1.1.2  mrg     test!(string, string);
    419  1.1.1.2  mrg     test!(ubyte[16], Object);
    420  1.1.1.2  mrg 
    421  1.1.1.2  mrg     static struct Small
    422  1.1.1.2  mrg     {
    423  1.1.1.2  mrg         ubyte[16] guid;
    424  1.1.1.2  mrg         string name;
    425  1.1.1.2  mrg     }
    426  1.1.1.2  mrg     test!(string, Small);
    427  1.1.1.2  mrg 
    428  1.1.1.2  mrg     static struct Large
    429  1.1.1.2  mrg     {
    430  1.1.1.2  mrg         ubyte[1024] data;
    431  1.1.1.2  mrg         string[412] names;
    432  1.1.1.2  mrg         ubyte[1024] moredata;
    433  1.1.1.2  mrg     }
    434  1.1.1.2  mrg     test!(Large, Large);
    435  1.1.1.2  mrg }
    436  1.1.1.2  mrg 
    437      1.1  mrg //==============================================================================
    438      1.1  mrg // Helper functions
    439      1.1  mrg //------------------------------------------------------------------------------
    440      1.1  mrg 
    441      1.1  mrg private size_t talign(size_t tsize, size_t algn) @safe pure nothrow @nogc
    442      1.1  mrg {
    443      1.1  mrg     immutable mask = algn - 1;
    444      1.1  mrg     assert(!(mask & algn));
    445      1.1  mrg     return (tsize + mask) & ~mask;
    446      1.1  mrg }
    447      1.1  mrg 
    448      1.1  mrg // mix hash to "fix" bad hash functions
    449      1.1  mrg private size_t mix(size_t h) @safe pure nothrow @nogc
    450      1.1  mrg {
    451      1.1  mrg     // final mix function of MurmurHash2
    452      1.1  mrg     enum m = 0x5bd1e995;
    453      1.1  mrg     h ^= h >> 13;
    454      1.1  mrg     h *= m;
    455      1.1  mrg     h ^= h >> 15;
    456      1.1  mrg     return h;
    457      1.1  mrg }
    458      1.1  mrg 
    459  1.1.1.2  mrg private size_t calcHash(scope const void* pkey, scope const TypeInfo keyti)
    460      1.1  mrg {
    461      1.1  mrg     immutable hash = keyti.getHash(pkey);
    462      1.1  mrg     // highest bit is set to distinguish empty/deleted from filled buckets
    463      1.1  mrg     return mix(hash) | HASH_FILLED_MARK;
    464      1.1  mrg }
    465      1.1  mrg 
    466  1.1.1.2  mrg private size_t nextpow2(const size_t n) pure nothrow @nogc
    467      1.1  mrg {
    468      1.1  mrg     import core.bitop : bsr;
    469      1.1  mrg 
    470      1.1  mrg     if (!n)
    471      1.1  mrg         return 1;
    472      1.1  mrg 
    473      1.1  mrg     const isPowerOf2 = !((n - 1) & n);
    474      1.1  mrg     return 1 << (bsr(n) + !isPowerOf2);
    475      1.1  mrg }
    476      1.1  mrg 
    477      1.1  mrg pure nothrow @nogc unittest
    478      1.1  mrg {
    479      1.1  mrg     //                            0, 1, 2, 3, 4, 5, 6, 7, 8,  9
    480      1.1  mrg     foreach (const n, const pow2; [1, 1, 2, 4, 4, 8, 8, 8, 8, 16])
    481      1.1  mrg         assert(nextpow2(n) == pow2);
    482      1.1  mrg }
    483      1.1  mrg 
    484      1.1  mrg //==============================================================================
    485      1.1  mrg // API Implementation
    486      1.1  mrg //------------------------------------------------------------------------------
    487      1.1  mrg 
    488      1.1  mrg /// Determine number of entries in associative array.
    489  1.1.1.2  mrg extern (C) size_t _aaLen(scope const AA aa) pure nothrow @nogc
    490      1.1  mrg {
    491      1.1  mrg     return aa ? aa.length : 0;
    492      1.1  mrg }
    493      1.1  mrg 
    494      1.1  mrg /******************************
    495      1.1  mrg  * Lookup *pkey in aa.
    496      1.1  mrg  * Called only from implementation of (aa[key]) expressions when value is mutable.
    497      1.1  mrg  * Params:
    498  1.1.1.2  mrg  *      paa = associative array opaque pointer
    499      1.1  mrg  *      ti = TypeInfo for the associative array
    500      1.1  mrg  *      valsz = ignored
    501      1.1  mrg  *      pkey = pointer to the key value
    502      1.1  mrg  * Returns:
    503      1.1  mrg  *      if key was in the aa, a mutable pointer to the existing value.
    504      1.1  mrg  *      If key was not in the aa, a mutable pointer to newly inserted value which
    505      1.1  mrg  *      is set to all zeros
    506      1.1  mrg  */
    507  1.1.1.2  mrg extern (C) void* _aaGetY(scope AA* paa, const TypeInfo_AssociativeArray ti,
    508  1.1.1.2  mrg     const size_t valsz, scope const void* pkey)
    509      1.1  mrg {
    510      1.1  mrg     bool found;
    511  1.1.1.2  mrg     return _aaGetX(paa, ti, valsz, pkey, found);
    512      1.1  mrg }
    513      1.1  mrg 
    514      1.1  mrg /******************************
    515      1.1  mrg  * Lookup *pkey in aa.
    516      1.1  mrg  * Called only from implementation of require
    517      1.1  mrg  * Params:
    518  1.1.1.2  mrg  *      paa = associative array opaque pointer
    519      1.1  mrg  *      ti = TypeInfo for the associative array
    520      1.1  mrg  *      valsz = ignored
    521      1.1  mrg  *      pkey = pointer to the key value
    522      1.1  mrg  *      found = true if the value was found
    523      1.1  mrg  * Returns:
    524      1.1  mrg  *      if key was in the aa, a mutable pointer to the existing value.
    525      1.1  mrg  *      If key was not in the aa, a mutable pointer to newly inserted value which
    526      1.1  mrg  *      is set to all zeros
    527      1.1  mrg  */
    528  1.1.1.2  mrg extern (C) void* _aaGetX(scope AA* paa, const TypeInfo_AssociativeArray ti,
    529  1.1.1.2  mrg     const size_t valsz, scope const void* pkey, out bool found)
    530      1.1  mrg {
    531      1.1  mrg     // lazily alloc implementation
    532  1.1.1.2  mrg     AA aa = *paa;
    533  1.1.1.2  mrg     if (aa is null)
    534  1.1.1.2  mrg     {
    535  1.1.1.2  mrg         aa = new Impl(ti);
    536  1.1.1.2  mrg         *paa = aa;
    537  1.1.1.2  mrg     }
    538      1.1  mrg 
    539      1.1  mrg     // get hash and bucket for key
    540      1.1  mrg     immutable hash = calcHash(pkey, ti.key);
    541      1.1  mrg 
    542      1.1  mrg     // found a value => return it
    543      1.1  mrg     if (auto p = aa.findSlotLookup(hash, pkey, ti.key))
    544      1.1  mrg     {
    545      1.1  mrg         found = true;
    546      1.1  mrg         return p.entry + aa.valoff;
    547      1.1  mrg     }
    548      1.1  mrg 
    549      1.1  mrg     auto p = aa.findSlotInsert(hash);
    550      1.1  mrg     if (p.deleted)
    551      1.1  mrg         --aa.deleted;
    552      1.1  mrg     // check load factor and possibly grow
    553      1.1  mrg     else if (++aa.used * GROW_DEN > aa.dim * GROW_NUM)
    554      1.1  mrg     {
    555      1.1  mrg         aa.grow(ti.key);
    556      1.1  mrg         p = aa.findSlotInsert(hash);
    557      1.1  mrg         assert(p.empty);
    558      1.1  mrg     }
    559      1.1  mrg 
    560      1.1  mrg     // update search cache and allocate entry
    561      1.1  mrg     aa.firstUsed = min(aa.firstUsed, cast(uint)(p - aa.buckets.ptr));
    562      1.1  mrg     p.hash = hash;
    563  1.1.1.2  mrg     p.entry = allocEntry(aa, pkey);
    564      1.1  mrg     // postblit for key
    565      1.1  mrg     if (aa.flags & Impl.Flags.keyHasPostblit)
    566      1.1  mrg     {
    567      1.1  mrg         import rt.lifetime : __doPostblit, unqualify;
    568      1.1  mrg 
    569      1.1  mrg         __doPostblit(p.entry, aa.keysz, unqualify(ti.key));
    570      1.1  mrg     }
    571      1.1  mrg     // return pointer to value
    572      1.1  mrg     return p.entry + aa.valoff;
    573      1.1  mrg }
    574      1.1  mrg 
    575      1.1  mrg /******************************
    576      1.1  mrg  * Lookup *pkey in aa.
    577      1.1  mrg  * Called only from implementation of (aa[key]) expressions when value is not mutable.
    578      1.1  mrg  * Params:
    579      1.1  mrg  *      aa = associative array opaque pointer
    580      1.1  mrg  *      keyti = TypeInfo for the key
    581      1.1  mrg  *      valsz = ignored
    582      1.1  mrg  *      pkey = pointer to the key value
    583      1.1  mrg  * Returns:
    584      1.1  mrg  *      pointer to value if present, null otherwise
    585      1.1  mrg  */
    586  1.1.1.2  mrg extern (C) inout(void)* _aaGetRvalueX(inout AA aa, scope const TypeInfo keyti, const size_t valsz,
    587  1.1.1.2  mrg     scope const void* pkey)
    588      1.1  mrg {
    589      1.1  mrg     return _aaInX(aa, keyti, pkey);
    590      1.1  mrg }
    591      1.1  mrg 
    592      1.1  mrg /******************************
    593      1.1  mrg  * Lookup *pkey in aa.
    594      1.1  mrg  * Called only from implementation of (key in aa) expressions.
    595      1.1  mrg  * Params:
    596      1.1  mrg  *      aa = associative array opaque pointer
    597      1.1  mrg  *      keyti = TypeInfo for the key
    598      1.1  mrg  *      pkey = pointer to the key value
    599      1.1  mrg  * Returns:
    600      1.1  mrg  *      pointer to value if present, null otherwise
    601      1.1  mrg  */
    602  1.1.1.2  mrg extern (C) inout(void)* _aaInX(inout AA aa, scope const TypeInfo keyti, scope const void* pkey)
    603      1.1  mrg {
    604      1.1  mrg     if (aa.empty)
    605      1.1  mrg         return null;
    606      1.1  mrg 
    607      1.1  mrg     immutable hash = calcHash(pkey, keyti);
    608      1.1  mrg     if (auto p = aa.findSlotLookup(hash, pkey, keyti))
    609      1.1  mrg         return p.entry + aa.valoff;
    610      1.1  mrg     return null;
    611      1.1  mrg }
    612      1.1  mrg 
    613  1.1.1.2  mrg /// Delete entry scope const AA, return true if it was present
    614  1.1.1.2  mrg extern (C) bool _aaDelX(AA aa, scope const TypeInfo keyti, scope const void* pkey)
    615      1.1  mrg {
    616      1.1  mrg     if (aa.empty)
    617      1.1  mrg         return false;
    618      1.1  mrg 
    619      1.1  mrg     immutable hash = calcHash(pkey, keyti);
    620      1.1  mrg     if (auto p = aa.findSlotLookup(hash, pkey, keyti))
    621      1.1  mrg     {
    622      1.1  mrg         // clear entry
    623      1.1  mrg         p.hash = HASH_DELETED;
    624      1.1  mrg         p.entry = null;
    625      1.1  mrg 
    626      1.1  mrg         ++aa.deleted;
    627  1.1.1.2  mrg         // `shrink` reallocates, and allocating from a finalizer leads to
    628  1.1.1.2  mrg         // InvalidMemoryError: https://issues.dlang.org/show_bug.cgi?id=21442
    629  1.1.1.2  mrg         if (aa.length * SHRINK_DEN < aa.dim * SHRINK_NUM && !GC.inFinalizer())
    630      1.1  mrg             aa.shrink(keyti);
    631      1.1  mrg 
    632      1.1  mrg         return true;
    633      1.1  mrg     }
    634      1.1  mrg     return false;
    635      1.1  mrg }
    636      1.1  mrg 
    637      1.1  mrg /// Remove all elements from AA.
    638      1.1  mrg extern (C) void _aaClear(AA aa) pure nothrow
    639      1.1  mrg {
    640      1.1  mrg     if (!aa.empty)
    641      1.1  mrg     {
    642  1.1.1.2  mrg         aa.clear();
    643      1.1  mrg     }
    644      1.1  mrg }
    645      1.1  mrg 
    646      1.1  mrg /// Rehash AA
    647  1.1.1.2  mrg extern (C) void* _aaRehash(AA* paa, scope const TypeInfo keyti) pure nothrow
    648      1.1  mrg {
    649  1.1.1.2  mrg     AA aa = *paa;
    650  1.1.1.2  mrg     if (!aa.empty)
    651  1.1.1.2  mrg         aa.resize(nextpow2(INIT_DEN * aa.length / INIT_NUM));
    652  1.1.1.2  mrg     return aa;
    653      1.1  mrg }
    654      1.1  mrg 
    655      1.1  mrg /// Return a GC allocated array of all values
    656  1.1.1.2  mrg extern (C) inout(void[]) _aaValues(inout AA aa, const size_t keysz, const size_t valsz,
    657      1.1  mrg     const TypeInfo tiValueArray) pure nothrow
    658      1.1  mrg {
    659      1.1  mrg     if (aa.empty)
    660      1.1  mrg         return null;
    661      1.1  mrg 
    662      1.1  mrg     import rt.lifetime : _d_newarrayU;
    663      1.1  mrg 
    664      1.1  mrg     auto res = _d_newarrayU(tiValueArray, aa.length).ptr;
    665      1.1  mrg     auto pval = res;
    666      1.1  mrg 
    667      1.1  mrg     immutable off = aa.valoff;
    668      1.1  mrg     foreach (b; aa.buckets[aa.firstUsed .. $])
    669      1.1  mrg     {
    670      1.1  mrg         if (!b.filled)
    671      1.1  mrg             continue;
    672      1.1  mrg         pval[0 .. valsz] = b.entry[off .. valsz + off];
    673      1.1  mrg         pval += valsz;
    674      1.1  mrg     }
    675      1.1  mrg     // postblit is done in object.values
    676      1.1  mrg     return (cast(inout(void)*) res)[0 .. aa.length]; // fake length, return number of elements
    677      1.1  mrg }
    678      1.1  mrg 
    679      1.1  mrg /// Return a GC allocated array of all keys
    680  1.1.1.2  mrg extern (C) inout(void[]) _aaKeys(inout AA aa, const size_t keysz, const TypeInfo tiKeyArray) pure nothrow
    681      1.1  mrg {
    682      1.1  mrg     if (aa.empty)
    683      1.1  mrg         return null;
    684      1.1  mrg 
    685      1.1  mrg     import rt.lifetime : _d_newarrayU;
    686      1.1  mrg 
    687      1.1  mrg     auto res = _d_newarrayU(tiKeyArray, aa.length).ptr;
    688      1.1  mrg     auto pkey = res;
    689      1.1  mrg 
    690      1.1  mrg     foreach (b; aa.buckets[aa.firstUsed .. $])
    691      1.1  mrg     {
    692      1.1  mrg         if (!b.filled)
    693      1.1  mrg             continue;
    694      1.1  mrg         pkey[0 .. keysz] = b.entry[0 .. keysz];
    695      1.1  mrg         pkey += keysz;
    696      1.1  mrg     }
    697      1.1  mrg     // postblit is done in object.keys
    698      1.1  mrg     return (cast(inout(void)*) res)[0 .. aa.length]; // fake length, return number of elements
    699      1.1  mrg }
    700      1.1  mrg 
    701      1.1  mrg // opApply callbacks are extern(D)
    702      1.1  mrg extern (D) alias dg_t = int delegate(void*);
    703      1.1  mrg extern (D) alias dg2_t = int delegate(void*, void*);
    704      1.1  mrg 
    705      1.1  mrg /// foreach opApply over all values
    706  1.1.1.2  mrg extern (C) int _aaApply(AA aa, const size_t keysz, dg_t dg)
    707      1.1  mrg {
    708      1.1  mrg     if (aa.empty)
    709      1.1  mrg         return 0;
    710      1.1  mrg 
    711      1.1  mrg     immutable off = aa.valoff;
    712      1.1  mrg     foreach (b; aa.buckets)
    713      1.1  mrg     {
    714      1.1  mrg         if (!b.filled)
    715      1.1  mrg             continue;
    716      1.1  mrg         if (auto res = dg(b.entry + off))
    717      1.1  mrg             return res;
    718      1.1  mrg     }
    719      1.1  mrg     return 0;
    720      1.1  mrg }
    721      1.1  mrg 
    722      1.1  mrg /// foreach opApply over all key/value pairs
    723  1.1.1.2  mrg extern (C) int _aaApply2(AA aa, const size_t keysz, dg2_t dg)
    724      1.1  mrg {
    725      1.1  mrg     if (aa.empty)
    726      1.1  mrg         return 0;
    727      1.1  mrg 
    728      1.1  mrg     immutable off = aa.valoff;
    729      1.1  mrg     foreach (b; aa.buckets)
    730      1.1  mrg     {
    731      1.1  mrg         if (!b.filled)
    732      1.1  mrg             continue;
    733      1.1  mrg         if (auto res = dg(b.entry, b.entry + off))
    734      1.1  mrg             return res;
    735      1.1  mrg     }
    736      1.1  mrg     return 0;
    737      1.1  mrg }
    738      1.1  mrg 
    739      1.1  mrg /// Construct an associative array of type ti from keys and value
    740      1.1  mrg extern (C) Impl* _d_assocarrayliteralTX(const TypeInfo_AssociativeArray ti, void[] keys,
    741      1.1  mrg     void[] vals)
    742      1.1  mrg {
    743      1.1  mrg     assert(keys.length == vals.length);
    744      1.1  mrg 
    745      1.1  mrg     immutable keysz = ti.key.tsize;
    746      1.1  mrg     immutable valsz = ti.value.tsize;
    747      1.1  mrg     immutable length = keys.length;
    748      1.1  mrg 
    749      1.1  mrg     if (!length)
    750      1.1  mrg         return null;
    751      1.1  mrg 
    752      1.1  mrg     auto aa = new Impl(ti, nextpow2(INIT_DEN * length / INIT_NUM));
    753      1.1  mrg 
    754      1.1  mrg     void* pkey = keys.ptr;
    755      1.1  mrg     void* pval = vals.ptr;
    756      1.1  mrg     immutable off = aa.valoff;
    757      1.1  mrg     uint actualLength = 0;
    758      1.1  mrg     foreach (_; 0 .. length)
    759      1.1  mrg     {
    760      1.1  mrg         immutable hash = calcHash(pkey, ti.key);
    761      1.1  mrg 
    762      1.1  mrg         auto p = aa.findSlotLookup(hash, pkey, ti.key);
    763      1.1  mrg         if (p is null)
    764      1.1  mrg         {
    765      1.1  mrg             p = aa.findSlotInsert(hash);
    766      1.1  mrg             p.hash = hash;
    767      1.1  mrg             p.entry = allocEntry(aa, pkey); // move key, no postblit
    768      1.1  mrg             aa.firstUsed = min(aa.firstUsed, cast(uint)(p - aa.buckets.ptr));
    769      1.1  mrg             actualLength++;
    770      1.1  mrg         }
    771      1.1  mrg         else if (aa.entryTI && hasDtor(ti.value))
    772      1.1  mrg         {
    773      1.1  mrg             // destroy existing value before overwriting it
    774      1.1  mrg             ti.value.destroy(p.entry + off);
    775      1.1  mrg         }
    776      1.1  mrg         // set hash and blit value
    777      1.1  mrg         auto pdst = p.entry + off;
    778      1.1  mrg         pdst[0 .. valsz] = pval[0 .. valsz]; // move value, no postblit
    779      1.1  mrg 
    780      1.1  mrg         pkey += keysz;
    781      1.1  mrg         pval += valsz;
    782      1.1  mrg     }
    783      1.1  mrg     aa.used = actualLength;
    784      1.1  mrg     return aa;
    785      1.1  mrg }
    786      1.1  mrg 
    787      1.1  mrg /// compares 2 AAs for equality
    788  1.1.1.2  mrg extern (C) int _aaEqual(scope const TypeInfo tiRaw, scope const AA aa1, scope const AA aa2)
    789      1.1  mrg {
    790  1.1.1.2  mrg     if (aa1 is aa2)
    791      1.1  mrg         return true;
    792      1.1  mrg 
    793      1.1  mrg     immutable len = _aaLen(aa1);
    794      1.1  mrg     if (len != _aaLen(aa2))
    795      1.1  mrg         return false;
    796      1.1  mrg 
    797      1.1  mrg     if (!len) // both empty
    798      1.1  mrg         return true;
    799      1.1  mrg 
    800      1.1  mrg     import rt.lifetime : unqualify;
    801      1.1  mrg 
    802      1.1  mrg     auto uti = unqualify(tiRaw);
    803      1.1  mrg     auto ti = *cast(TypeInfo_AssociativeArray*)&uti;
    804      1.1  mrg     // compare the entries
    805      1.1  mrg     immutable off = aa1.valoff;
    806      1.1  mrg     foreach (b1; aa1.buckets)
    807      1.1  mrg     {
    808      1.1  mrg         if (!b1.filled)
    809      1.1  mrg             continue;
    810      1.1  mrg         auto pb2 = aa2.findSlotLookup(b1.hash, b1.entry, ti.key);
    811      1.1  mrg         if (pb2 is null || !ti.value.equals(b1.entry + off, pb2.entry + off))
    812      1.1  mrg             return false;
    813      1.1  mrg     }
    814      1.1  mrg     return true;
    815      1.1  mrg }
    816      1.1  mrg 
    817      1.1  mrg /// compute a hash
    818  1.1.1.2  mrg extern (C) hash_t _aaGetHash(scope const AA* paa, scope const TypeInfo tiRaw) nothrow
    819      1.1  mrg {
    820  1.1.1.2  mrg     const AA aa = *paa;
    821  1.1.1.2  mrg 
    822      1.1  mrg     if (aa.empty)
    823      1.1  mrg         return 0;
    824      1.1  mrg 
    825      1.1  mrg     import rt.lifetime : unqualify;
    826      1.1  mrg 
    827      1.1  mrg     auto uti = unqualify(tiRaw);
    828      1.1  mrg     auto ti = *cast(TypeInfo_AssociativeArray*)&uti;
    829      1.1  mrg     immutable off = aa.valoff;
    830      1.1  mrg     auto keyHash = &ti.key.getHash;
    831      1.1  mrg     auto valHash = &ti.value.getHash;
    832      1.1  mrg 
    833      1.1  mrg     size_t h;
    834      1.1  mrg     foreach (b; aa.buckets)
    835      1.1  mrg     {
    836      1.1  mrg         // use addition here, so that hash is independent of element order
    837  1.1.1.2  mrg         if (b.filled)
    838  1.1.1.2  mrg             h += hashOf(valHash(b.entry + off), keyHash(b.entry));
    839      1.1  mrg     }
    840      1.1  mrg 
    841      1.1  mrg     return h;
    842      1.1  mrg }
    843      1.1  mrg 
    844      1.1  mrg /**
    845      1.1  mrg  * _aaRange implements a ForwardRange
    846      1.1  mrg  */
    847      1.1  mrg struct Range
    848      1.1  mrg {
    849      1.1  mrg     Impl* impl;
    850      1.1  mrg     size_t idx;
    851      1.1  mrg     alias impl this;
    852      1.1  mrg }
    853      1.1  mrg 
    854      1.1  mrg extern (C) pure nothrow @nogc @safe
    855      1.1  mrg {
    856  1.1.1.2  mrg     Range _aaRange(return scope AA aa)
    857      1.1  mrg     {
    858      1.1  mrg         if (!aa)
    859      1.1  mrg             return Range();
    860      1.1  mrg 
    861      1.1  mrg         foreach (i; aa.firstUsed .. aa.dim)
    862      1.1  mrg         {
    863      1.1  mrg             if (aa.buckets[i].filled)
    864  1.1.1.2  mrg                 return Range(aa, i);
    865      1.1  mrg         }
    866      1.1  mrg         return Range(aa, aa.dim);
    867      1.1  mrg     }
    868      1.1  mrg 
    869      1.1  mrg     bool _aaRangeEmpty(Range r)
    870      1.1  mrg     {
    871      1.1  mrg         return r.impl is null || r.idx >= r.dim;
    872      1.1  mrg     }
    873      1.1  mrg 
    874      1.1  mrg     void* _aaRangeFrontKey(Range r)
    875      1.1  mrg     {
    876      1.1  mrg         assert(!_aaRangeEmpty(r));
    877      1.1  mrg         if (r.idx >= r.dim)
    878      1.1  mrg             return null;
    879      1.1  mrg         return r.buckets[r.idx].entry;
    880      1.1  mrg     }
    881      1.1  mrg 
    882      1.1  mrg     void* _aaRangeFrontValue(Range r)
    883      1.1  mrg     {
    884      1.1  mrg         assert(!_aaRangeEmpty(r));
    885      1.1  mrg         if (r.idx >= r.dim)
    886      1.1  mrg             return null;
    887      1.1  mrg 
    888      1.1  mrg         auto entry = r.buckets[r.idx].entry;
    889      1.1  mrg         return entry is null ?
    890      1.1  mrg             null :
    891      1.1  mrg             (() @trusted { return entry + r.valoff; } ());
    892      1.1  mrg     }
    893      1.1  mrg 
    894      1.1  mrg     void _aaRangePopFront(ref Range r)
    895      1.1  mrg     {
    896      1.1  mrg         if (r.idx >= r.dim) return;
    897      1.1  mrg         for (++r.idx; r.idx < r.dim; ++r.idx)
    898      1.1  mrg         {
    899      1.1  mrg             if (r.buckets[r.idx].filled)
    900      1.1  mrg                 break;
    901      1.1  mrg         }
    902      1.1  mrg     }
    903      1.1  mrg }
    904      1.1  mrg 
    905  1.1.1.2  mrg // Most tests are now in test_aa.d
    906      1.1  mrg 
    907      1.1  mrg // test postblit for AA literals
    908      1.1  mrg unittest
    909      1.1  mrg {
    910      1.1  mrg     static struct T
    911      1.1  mrg     {
    912      1.1  mrg         ubyte field;
    913      1.1  mrg         static size_t postblit, dtor;
    914      1.1  mrg         this(this)
    915      1.1  mrg         {
    916      1.1  mrg             ++postblit;
    917      1.1  mrg         }
    918      1.1  mrg 
    919      1.1  mrg         ~this()
    920      1.1  mrg         {
    921      1.1  mrg             ++dtor;
    922      1.1  mrg         }
    923      1.1  mrg     }
    924      1.1  mrg 
    925      1.1  mrg     T t;
    926      1.1  mrg     auto aa1 = [0 : t, 1 : t];
    927      1.1  mrg     assert(T.dtor == 0 && T.postblit == 2);
    928      1.1  mrg     aa1[0] = t;
    929      1.1  mrg     assert(T.dtor == 1 && T.postblit == 3);
    930      1.1  mrg 
    931      1.1  mrg     T.dtor = 0;
    932      1.1  mrg     T.postblit = 0;
    933      1.1  mrg 
    934      1.1  mrg     auto aa2 = [0 : t, 1 : t, 0 : t]; // literal with duplicate key => value overwritten
    935      1.1  mrg     assert(T.dtor == 1 && T.postblit == 3);
    936      1.1  mrg 
    937      1.1  mrg     T.dtor = 0;
    938      1.1  mrg     T.postblit = 0;
    939      1.1  mrg 
    940      1.1  mrg     auto aa3 = [t : 0];
    941      1.1  mrg     assert(T.dtor == 0 && T.postblit == 1);
    942      1.1  mrg     aa3[t] = 1;
    943      1.1  mrg     assert(T.dtor == 0 && T.postblit == 1);
    944      1.1  mrg     aa3.remove(t);
    945      1.1  mrg     assert(T.dtor == 0 && T.postblit == 1);
    946      1.1  mrg     aa3[t] = 2;
    947      1.1  mrg     assert(T.dtor == 0 && T.postblit == 2);
    948      1.1  mrg 
    949      1.1  mrg     // dtor will be called by GC finalizers
    950      1.1  mrg     aa1 = null;
    951      1.1  mrg     aa2 = null;
    952      1.1  mrg     aa3 = null;
    953      1.1  mrg     GC.runFinalizers((cast(char*)(&entryDtor))[0 .. 1]);
    954      1.1  mrg     assert(T.dtor == 6 && T.postblit == 2);
    955      1.1  mrg }
    956