Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /**
- * Written in the D programming language.
- * This module provides an back-end implementation of associative array
- *
- * Copyright: Copyright Igor Stepanov 2013-2014.
- * License: <a href="http://www.boost.org/LICENSE_1_0.txt">Boost License 1.0</a>.
- * Authors: Igor Stepanov
- * Source: $(DRUNTIMESRC core/internal/_aa.d)
- */
- module aa;
- import core.internal.traits;
- import core.exception;
- import core.memory;
- enum Mutability
- {
- Mutable,
- Const,
- Immutable,
- //Shared,
- //ConstShared
- }
- struct AssociativeArray(Key, Value)
- {
- private enum isLvlValueConst = lvlOpEqualsArgModififer!Value == OpEqualsArg.Const;
- private enum isRvlValueConst = rvlOpEqualsArgModififer!Value == OpEqualsArg.Const;
- private enum LoadFactor = 0.6;
- this(T...)(auto ref T args) if (T.length >= 2 && T.length % 2 == 0)
- {
- impl = new Impl();
- impl.nodes = args.length / 2;
- impl.buckets = newBuckets(findGoodLength(cast(size_t)(impl.nodes / LoadFactor)));
- auto len = impl.buckets.length;
- foreach (i; staticIota!(0, args.length / 2))
- {
- static if (is(typeof(args[2*i]) == Key))
- {
- alias key = args[2*i];
- }
- else
- {
- Key key = args[2*i];
- }
- static if (is(typeof(args[2*i + 1]) == Value))
- {
- alias value = args[2*i + 1];
- }
- else
- {
- Value value = args[2*i + 1];
- }
- size_t key_hash = hashOf(key);
- size_t depth;
- size_t idx = findBucket!true(key_hash, key, depth);
- assert(impl.buckets[idx].occupation == Empty, "duplicate key");
- impl.buckets[idx].setKey!true(key);
- impl.buckets[idx].setValue!true(value);
- impl.buckets[idx].occupation == Occupied;
- //impl.buckets[idx].depth = depth;
- size_t base_idx = key_hash & (impl.buckets.length - 1);
- if (impl.buckets[base_idx].depth < depth)
- impl.buckets[base_idx].depth = depth;
- }
- }
- void init(size_t size)
- {
- impl = new Impl();
- impl.nodes = 0;
- impl.buckets = newBuckets(findGoodLength(cast(size_t)(size / LoadFactor)));
- }
- @property @trusted pure nothrow const
- size_t length()
- out (result)
- {
- size_t len = 0;
- if (impl)
- {
- for (size_t i = 0; i < impl.buckets.length; i++)
- {
- if (impl.buckets[i].occupation == Occupied)
- {
- len++;
- }
- }
- }
- assert(len == result);
- }
- body
- {
- return impl ? impl.nodes : 0;
- }
- //cannot implicitly convert expression ... of type const(Thread)* to inout(Thread)*
- //BUG?
- inout(Value)* opIn_r()(auto ref in Key key) inout @trusted
- {
- auto e = getRValue(key);
- return cast(typeof(return))(e ? &e.value : null);
- }
- ref Value opIndex()(auto ref in Key key)
- {
- auto v = getRValue(key);
- if (!v)
- onRangeError();
- else
- return v.value;
- assert(0);
- }
- //cannot implicitly convert expression ... of type const(Thread) to inout(Thread)
- ref const(Value) opIndex()(auto ref in Key key) const
- {
- auto v = getRValue(key);
- if (!v)
- onRangeError();
- else
- return v.value;
- assert(0);
- }
- ref Value opIndexAssign()(auto ref Value value, in auto ref Key key)
- {
- auto v = getLValue(key);
- assert(v);
- v.setValue!false(value);
- return v.value;
- }
- bool remove()(auto ref in Key key)
- {
- if (!impl)
- {
- return false;
- }
- auto key_hash = hashOf(key);
- size_t initial_hash = key_hash & (impl.buckets.length - 1);
- size_t mdepth = impl.buckets[initial_hash].depth;
- size_t j = 1;
- size_t i = initial_hash;
- for (; j <= mdepth; ++j)
- {
- if (impl.buckets[i].occupation == Occupied &&
- impl.buckets[i].hash == key_hash &&
- impl.buckets[i].key == key)
- {
- break;
- }
- i = (i + j) & (impl.buckets.length - 1);
- }
- if (j > mdepth)
- {
- return false;
- }
- else
- {
- size_t to_remove = i;
- size_t last = i;
- size_t last_j = j - 1;
- for (; j <= mdepth; ++j)
- {
- i = (i + j) & (impl.buckets.length - 1);
- if (impl.buckets[i].occupation == Occupied &&
- (impl.buckets[i].hash & (impl.buckets.length - 1)) == initial_hash)
- {
- last = i;
- last_j = j;
- }
- }
- if (last != to_remove)
- {
- impl.buckets[to_remove].swapKV(impl.buckets[last]);
- //impl.buckets[to_remove]._depth = impl.buckets[last]._depth;
- }
- impl.buckets[last].occupation = Empty;
- impl.buckets[last].dispose();
- impl.nodes--;
- //impl.buckets[initial_hash].depth = mdepth - (mdepth - last_j + 1);
- return true;
- }
- }
- AssociativeArray rehash() @safe
- {
- return _rehash();
- }
- AssociativeArray dup() const
- {
- return _dup();
- }
- static if (isCopyConstructable!Key)
- {
- Key[] keys() @property
- {
- if (!length) return null;
- size_t len = 0;
- Key[] elems;
- if (!__ctfe)
- elems.reserve(impl.nodes);
- for (size_t i = 0; i < impl.buckets.length; i++)
- {
- if (impl.buckets[i].occupation == Occupied)
- {
- elems ~= impl.buckets[i].key;
- len++;
- }
- }
- assert(len == impl.nodes);
- return elems;
- }
- const(Key)[] keys() const @property
- {
- return cast(const)(cast()this).keys;
- }
- inout(Key)[] inout_keys() inout @property
- {
- return cast(inout(Key)[])keys;
- }
- }
- else
- {
- @disable Key[] keys() @property;
- @disable const(Key)[] keys() const @property;
- @disable inout(Key)[] inout_keys() inout @property;
- }
- static if (isCopyConstructable!Value)
- {
- Value[] values() @property
- {
- if (!length) return null;
- size_t len = 0;
- Value[] elems;
- if (!__ctfe)
- elems.reserve(impl.nodes);
- //Do I need to set GC.BlkAttr.NO_SCAN?
- //a.length = _aaLen(aa);
- //a.ptr = cast(byte*) GC.malloc(a.length * valuesize,
- // valuesize < (void*).sizeof ? GC.BlkAttr.NO_SCAN : 0);
- for (size_t i = 0; i < impl.buckets.length; i++)
- {
- if (impl.buckets[i].occupation == Occupied)
- {
- elems ~= impl.buckets[i].value;
- len++;
- }
- }
- assert(len == impl.nodes);
- return elems;
- }
- const(Value)[] values() const @property
- {
- return cast(const)(cast()this).values;
- }
- inout(Value)[] inout_values() inout @property
- {
- return cast(inout(Value)[])values;
- }
- }
- else
- {
- @disable Value[] values() @property;
- @disable const(Value)[] values() const @property;
- @disable inout(Value)[] inout_values() inout @property;
- }
- int opApply(scope int delegate(ref Value) dg)
- {
- if (!impl)
- {
- return 0;
- }
- for (size_t i = 0; i < impl.buckets.length; i++)
- {
- if (impl.buckets[i].occupation == Occupied)
- {
- auto result = dg(impl.buckets[i].value);
- if (result)
- return result;
- }
- }
- return 0;
- }
- int opApply(scope int delegate(ref Key, ref Value) dg)
- {
- if (!impl)
- {
- return 0;
- }
- for (size_t i = 0; i < impl.buckets.length; i++)
- {
- if (impl.buckets[i].occupation == Occupied)
- {
- auto result = dg(impl.buckets[i].key, impl.buckets[i].value);
- if (result)
- return result;
- }
- }
- return 0;
- }
- int opApply(scope int delegate(ref const(Value)) dg) const
- {
- if (!impl)
- {
- return 0;
- }
- for (size_t i = 0; i < impl.buckets.length; i++)
- {
- if (impl.buckets[i].occupation == Occupied)
- {
- auto result = dg(impl.buckets[i].value);
- if (result)
- return result;
- }
- }
- return 0;
- }
- int opApply(scope int delegate(ref const(Key), ref const(Value)) dg) const
- {
- if (!impl)
- {
- return 0;
- }
- for (size_t i = 0; i < impl.buckets.length; i++)
- {
- if (impl.buckets[i].occupation == Occupied)
- {
- auto result = dg(impl.buckets[i].key, impl.buckets[i].value);
- if (result)
- return result;
- }
- }
- return 0;
- }
- ref AssociativeArray opAssign(typeof(null)) @safe pure nothrow
- {
- impl = null;
- return this;
- }
- inout(Value) get(in Key key, lazy inout(Value) defaultValue) inout
- {
- auto p = key in this;
- return p ? *p : defaultValue;
- }
- auto byKey() @safe pure nothrow
- {
- return getRange!(KeyValue.Key)();
- }
- auto byValue() @safe pure nothrow
- {
- return getRange!(KeyValue.Value)();
- }
- auto byKey() @safe pure nothrow const
- {
- return getConstRange!(KeyValue.Key)();
- }
- auto byValue() @safe pure nothrow const
- {
- return getConstRange!(KeyValue.Value)();
- }
- //AA allows a values which have non-const opEquals, or opEquals which takes mutable rvl arg
- //If Value.opEquals is non-const, AssociativeArray.opEquals should be mutable too.
- //If Value.opEquals can't take a const value, AssociativeArray.opEquals can't take const argument too.
- //AA Keys always should have const opEquals which takes const argument
- private static string getOpEqualsCode()
- {
- string fbody = "";
- if (isRvlValueConst)
- {
- fbody ~= "bool opEquals(const AssociativeArray rvl)";
- }
- else
- {
- fbody ~= "bool opEquals(AssociativeArray rvl)";
- }
- if (isLvlValueConst)
- fbody ~= " const\n";
- else
- fbody ~= "\n";
- fbody ~= q{
- {
- if (impl.nodes != rvl.impl.nodes)
- return false;
- if (!impl.nodes)
- return true;
- for (size_t i = 0; i < impl.buckets.length; i++)
- {
- if (impl.buckets[i].occupation == Occupied)
- {
- size_t depth;
- size_t idx = rvl.findBucket!false(impl.buckets[i].hash, impl.buckets[i].key, depth);
- if (idx == -1)
- return false;
- if (!impl.buckets[i].isEqualsValue(rvl.impl.buckets[idx].value))
- return false;
- }
- }
- return true;
- }
- };
- return fbody;
- }
- mixin(getOpEqualsCode());
- private:
- //@trusted is needed because GC functions are used
- AssociativeArray _rehash() @trusted
- {
- AssociativeArray ret;
- if (!impl)
- return ret;
- if (!impl.nodes)
- {
- if (!__ctfe)
- {
- GC.free(impl.buckets.ptr);
- impl.buckets = new Entry[findGoodLength(0)];
- }
- return this;
- }
- Entry[] old_buckets = impl.buckets;
- auto len = findGoodLength(cast(size_t)(impl.nodes / LoadFactor) + 1);
- impl.buckets = newBuckets(len);
- for (size_t i = 0; i < old_buckets.length; i++)
- {
- if (old_buckets[i].occupation == Occupied)
- {
- size_t depth;
- size_t idx = findBucket!true(old_buckets[i].hash, old_buckets[i].key, depth);
- impl.buckets[idx].swapKV(old_buckets[i]);
- impl.buckets[idx].occupation = Occupied;
- //impl.buckets[idx].depth = depth;
- size_t base_idx = old_buckets[i].hash & (impl.buckets.length - 1);
- if (impl.buckets[base_idx].depth < depth)
- impl.buckets[base_idx].depth = depth;
- }
- }
- if (!__ctfe)
- GC.free(old_buckets.ptr);
- return this;
- }
- AssociativeArray _dup() const @trusted
- {
- AssociativeArray ret;
- if (!impl)
- return ret;
- ret.impl.buckets = newBuckets(ret.impl.buckets.length);
- (cast(void*)ret.impl.buckets.ptr)[0 .. impl.buckets.length * Entry.sizeof] =
- (cast(void*)impl.buckets.ptr)[0 .. impl.buckets.length * Entry.sizeof];
- foreach (i, ref cur; ret.impl.buckets)
- {
- cur.setKey!true(impl.buckets[i].key);
- cur.setKey!true(impl.buckets[i].value);
- }
- return ret;
- }
- static Impl* newImpl() @safe pure
- {
- auto impl = new Impl();
- impl.buckets = newBuckets(findGoodLength(0));
- return impl;
- }
- Entry* getLValue(ref const(Unqual!Key) pkey)
- {
- if (!impl)
- {
- impl = newImpl();
- }
- auto key_hash = hashOf(pkey);
- size_t depth;
- size_t idx = findBucket!true(key_hash, pkey, depth);
- if (impl.buckets[idx].occupation == Empty)
- {
- if (((impl.nodes) / LoadFactor) > impl.buckets.length)
- {
- grow();
- depth = 0;
- idx = findBucket!true(key_hash, pkey, depth);
- }
- impl.nodes++;
- impl.buckets[idx].occupation = Occupied;
- impl.buckets[idx].hash = key_hash;
- impl.buckets[idx].setKey!true(pkey);
- //impl.buckets[idx].depth = depth;
- size_t base_idx = key_hash & (impl.buckets.length - 1);
- if (impl.buckets[base_idx].depth < depth)
- impl.buckets[base_idx].depth = depth;
- }
- return &impl.buckets[idx];
- }
- //!CTFE BUG: cannot cast &Entry(...) to inout(Entry)*
- Entry* getRValue(ref const(Unqual!Key) pkey)
- {
- if (!impl)
- return null;
- auto key_hash = hashOf(pkey);
- size_t depth;
- size_t idx = findBucket(key_hash, pkey, depth);
- if (idx == -1)
- return null;
- return &impl.buckets[idx];
- }
- const(Entry)* getRValue(ref const(Unqual!Key) pkey) const
- {
- if (!impl)
- return null;
- auto key_hash = hashOf(pkey);
- size_t depth;
- size_t idx = findBucket(key_hash, pkey, depth);
- if (idx == -1)
- return null;
- return &impl.buckets[idx];
- }
- size_t findBucket(bool insert = false)(size_t hash, ref const(Unqual!Key) pkey, ref size_t depth) const
- {
- size_t initial_hash = hash & (impl.buckets.length - 1);
- size_t mdepth = impl.buckets[initial_hash].depth;
- static if (insert)
- {
- size_t first_empty_post = -1;
- }
- size_t j = 1;
- size_t i = initial_hash;
- for (; j <= mdepth; i = (i + j) & (impl.buckets.length - 1), ++j)
- {
- static if (insert)
- {
- if (impl.buckets[i].occupation == Empty)
- {
- if (first_empty_post == -1)
- first_empty_post = i;
- continue;
- }
- }
- if (impl.buckets[i].occupation == Occupied &&
- impl.buckets[i].hash == hash &&
- impl.buckets[i].key == pkey)
- {
- depth = j;
- return i;
- }
- }
- static if (insert)
- {
- if (first_empty_post != -1)
- return first_empty_post;
- for (; ; i = (i + j) & (impl.buckets.length - 1), ++j)
- {
- if (impl.buckets[i].occupation == Empty)
- {
- depth = j;
- return i;
- }
- }
- assert(0);
- }
- else
- {
- return -1;
- }
- }
- void grow()
- {
- auto obuckets = impl.buckets;
- impl.buckets = newBuckets(obuckets.length * 8);
- for (size_t i = 0; i < obuckets.length; ++i)
- {
- if (obuckets[i].occupation != Occupied)
- continue;
- size_t hash = obuckets[i].hash;
- size_t depth;
- size_t newidx = findBucket!true(hash, obuckets[i].key, depth);
- auto odepth = impl.buckets[newidx].depth;
- auto xx_key = obuckets[i].key;
- auto xx_val = obuckets[i].value;
- impl.buckets[newidx].swapKV(obuckets[i]);
- assert(odepth == impl.buckets[newidx].depth);
- assert(xx_key == impl.buckets[newidx].key);
- assert(xx_val == impl.buckets[newidx].value);
- assert(hash == impl.buckets[newidx].hash);
- impl.buckets[newidx].occupation = Occupied;
- size_t base_idx = hash & (impl.buckets.length - 1);
- if (impl.buckets[base_idx].depth < depth)
- impl.buckets[base_idx].depth = depth;
- }
- }
- static Entry[] newBuckets(in size_t len) @trusted pure nothrow
- {
- if (!__ctfe)
- {
- auto ptr = cast(Entry*) GC.calloc(
- len * (Entry).sizeof, GC.BlkAttr.NO_INTERIOR);
- return ptr[0..len];
- }
- else
- {
- return new Entry[len];
- }
- }
- static size_t findGoodLength(size_t idx) @safe pure nothrow
- {
- //BUG, Why core.stdc.math.log2 is impure?
- size_t l = 16;
- while (l < idx)
- l *= 2;
- return l;
- }
- Range!(Mutability.Mutable, kv) getRange(KeyValue kv)() @safe pure nothrow
- {
- Range!(Mutability.Mutable, kv) res;
- res.impl = impl;
- res.current = -1;
- if (!length)
- return res;
- for (size_t i = 0; i < impl.buckets.length; i++)
- {
- if (impl.buckets[i].occupation == Occupied)
- {
- res.current = i;
- break;
- }
- }
- return res;
- }
- Range!(Mutability.Const, kv) getConstRange(KeyValue kv)() @safe pure nothrow const
- {
- Range!(Mutability.Const, kv) res;
- res.impl = impl;
- res.current = -1;
- if (!length)
- return res;
- for (size_t i = 0; i < impl.buckets.length; i++)
- {
- if (impl.buckets[i].occupation == Occupied)
- {
- res.current = i;
- break;
- }
- }
- return res;
- }
- size_t toHash() const nothrow @trusted
- {
- try
- {
- if (!length) return 0;
- size_t h = 0;
- // The computed hash is independent of the foreach traversal order.
- foreach (key, ref val; this)
- {
- size_t[2] hpair;
- hpair[0] = key.hashOf();
- hpair[1] = val.hashOf();
- h ^= hashOf(hpair[1], hashOf(hpair[0]));
- }
- return h;
- }
- catch(Throwable th)
- {
- return 0;
- }
- }
- static struct Range(Mutability mutability, KeyValue ckv = KeyValue.Unknown)
- {
- size_t current;
- static if (mutability == Mutability.Mutable)
- {
- Impl* impl;
- alias RetKey = Key;
- alias RetValue = Value;
- }
- else
- {
- const(Impl)* impl;
- alias RetKey = const(Key);
- alias RetValue = const(Value);
- }
- static Range fromAARange(AARange r) @nogc
- {
- Range ret;
- ret.impl = cast(typeof(ret.impl))r.impl;
- ret.current = cast(typeof(ret.current))r.current;
- return ret;
- }
- AARange toAARange() @nogc
- {
- AARange ret;
- ret.impl = cast(void*)impl;
- ret.current = cast(void*)current;
- return ret;
- }
- @property bool empty() @nogc
- {
- return !impl || current == -1;
- }
- static if (ckv == KeyValue.Key)
- {
- @property ref RetKey front() @nogc//really ref Key? may be ref const(Key) or Key?
- in
- {
- assert(current != -1);
- }
- body
- {
- return impl.buckets[current].key;
- }
- }
- else static if (ckv == KeyValue.Value)
- {
- @property ref RetValue front() @nogc
- in
- {
- assert(current != -1);
- }
- body
- {
- return impl.buckets[current].value;
- }
- }
- else
- {
- @property ref RetKey front(KeyValue kv)() @nogc if (kv == KeyValue.Key && ckv == KeyValue.Unknown) //really ref Key? may be ref const(Key) or Key?
- in
- {
- assert(current != -1);
- }
- body
- {
- return impl.buckets[current].key;
- }
- @property ref RetValue front(KeyValue kv)() @nogc if (kv == KeyValue.Value && ckv == KeyValue.Unknown)
- in
- {
- assert(current != -1);
- }
- body
- {
- return impl.buckets[current].value;
- }
- }
- void popFront() @nogc
- in
- {
- assert(impl);
- assert(current);
- }
- body
- {
- current++;
- while (current < impl.buckets.length)
- {
- if (impl.buckets[current].occupation == Occupied)
- {
- return;
- }
- current++;
- }
- current = -1;
- }
- Range save() @nogc
- {
- return this;
- }
- }
- static struct Impl
- {
- immutable(AssociativeArrayHandle)* handle = &AssociativeArray.handle;
- Entry[] buckets;
- size_t nodes; // total number of entries
- this(Impl i)
- {
- buckets = i.buckets;
- nodes = i.nodes;
- }
- }
- private enum ubyte Empty = 0b00;
- private enum ubyte Occupied = 0b01;
- static struct Entry
- {
- size_t hash;
- Key key;
- Value value;
- size_t _depth;
- @disable this(this);
- void setValue(bool isnew, V)(ref V value)
- {
- static if (isAssignable!(Value, V))
- {
- static if (false && isnew)
- {
- if (!__ctfe)
- _emplace!isnew(this.value, value);
- else
- this.value = value;
- }
- else
- {
- this.value = value;
- }
- }
- else
- {
- _emplace!isnew(this.value, value);
- }
- }
- void setKey(bool isnew, K)(ref K key)
- {
- static if (isAssignable!(Key, K))
- {
- static if (false && isnew)
- {
- if (!__ctfe)
- _emplace!isnew(this.key, key);
- else
- this.key = key;
- }
- else
- {
- this.key = key;
- }
- }
- else
- {
- _emplace!isnew(this.key, key);
- }
- }
- void dispose()
- {
- static if (isAssignable!(Key, Key))
- {
- this.key = Key.init;
- }
- else
- {
- _dispose(this.key);
- }
- static if (isAssignable!(Value, Value))
- {
- this.value = Value.init;
- }
- else
- {
- _dispose(this.value);
- }
- }
- //swap data without calling postblits
- void swapKV(ref Entry rvl) @trusted
- {
- ubyte[Entry.sizeof] buff;
- buff[] = (cast(ubyte*)&this)[0 .. Entry.sizeof];
- (cast(ubyte*)&this)[0 .. Entry.sizeof] = (cast(ubyte*)&rvl)[0 .. Entry.sizeof];
- (cast(ubyte*)&rvl)[0 .. Entry.sizeof] = buff[];
- //swap aux-fields back
- rvl._depth ^= _depth;
- _depth ^= rvl._depth;
- rvl._depth ^= _depth;
- }
- @property ubyte occupation() const
- {
- return _depth & 0b11;
- }
- @property ubyte occupation(ubyte o)
- {
- _depth &= ~0b11;
- _depth |= o & 0b11;
- return _depth & 0b11;
- }
- @property size_t depth() const
- {
- return _depth >> 2;
- }
- @property size_t depth(size_t d)
- {
- _depth = (d << 2) | (_depth & 0b11);
- return d;
- }
- bool isEqualsKey(ref const(Unqual!Key) key) const
- {
- return this.key == key;
- }
- static if(isLvlValueConst && isRvlValueConst)
- {
- bool isEqualsValue(ref const(Unqual!Value) value) const
- {
- return this.value == value;
- }
- }
- else static if(!isLvlValueConst && isRvlValueConst)
- {
- bool isEqualsValue(ref const(Unqual!Value) value)
- {
- return this.value == value;
- }
- }
- else static if(isLvlValueConst && !isRvlValueConst)
- {
- bool isEqualsValue(ref Value value) const
- {
- return this.value == value;
- }
- }
- else
- {
- bool isEqualsValue(ref Value value)
- {
- return this.value == value;
- }
- }
- }
- static immutable AssociativeArrayHandle handle = AssociativeArrayHandle(&_aaLen,
- &_aaGetX,
- &_aaGetRvalueX,
- &_aaDelX,
- &_aaApply,
- &_aaApply2,
- &_aaEqual,
- &_aaValues,
- &_aaKeys,
- &_aaRehash,
- &_aaDup,
- &_aaRange,
- &_aaRangeEmpty,
- &_aaRangeFrontKey,
- &_aaRangeFrontValue,
- &_aaRangePopFront,
- &_aaGetHash);
- Impl* impl = null;
- /**
- Compiler interface functions:
- Those functions are defined for compatibility with old compiler extern(C) interface.
- `Impl` contains a table of functions (unique for each AssociativeArray template instance),
- and global extern(C) _aaXXX functions accesses to AssociativeArray through this table.
- Some of this functions undeservedly marked with @trusted: for example _aaGetX calculates key hash,
- and this code can be unsafe. However extern(C) interface doesn't know static AssociativeArray type,
- thus it can't determine own safety, but this function can be called from @safe code.
- This is fundamental trouble of current AA implementation and it's not relevant with this work.
- */
- static size_t _aaLen(in void* aa) @trusted pure nothrow
- {
- auto _this = *cast(AssociativeArray*)&aa;
- return _this.length;
- }
- static size_t _aaGetHash(in void* aa)
- {
- auto _this = *cast(AssociativeArray*)&aa;
- return _this.toHash();
- }
- static void* _aaGetX(void** aa, in void* pkey) @trusted
- {
- auto _this = *cast(AssociativeArray*)aa;
- auto ret = cast(void*)&_this.getLValue(*cast(Key*)pkey).value;
- *aa = *cast(void**)&_this;
- return ret;
- }
- static inout(void)* _aaGetRvalueX(inout void* aa, in void* pkey) @trusted
- {
- auto _this = *cast(AssociativeArray*)&aa;
- auto e = _this.getRValue(*cast(Key*)pkey);
- return cast(inout(void)*)(e ? &e.value : null);
- }
- static bool _aaDelX(void* aa, in void* pkey) @trusted
- {
- auto _this = *cast(AssociativeArray*)&aa;
- return _this.remove(*cast(Key*)pkey);
- }
- static int _aaApply(void* aa, dg_t dg)
- {
- auto _this = *cast(AssociativeArray*)&aa;
- return _this.opApply(cast(int delegate(ref Value))dg);
- }
- static int _aaApply2(void* aa, dg2_t dg)
- {
- auto _this = *cast(AssociativeArray*)&aa;
- return _this.opApply(cast(int delegate(ref Key, ref Value))dg);
- }
- static int _aaEqual(in void* e1, in void* e2) @trusted
- {
- auto lvl = *cast(AssociativeArray*)&e1;
- auto rvl = *cast(AssociativeArray*)&e2;
- return lvl == rvl;
- }
- static if (isCopyConstructable!Value)
- {
- static inout(void)[] _aaValues(inout void* aa)
- {
- auto _this = *cast(AssociativeArray*)&aa;
- auto val = _this.values;
- return *cast(inout(void)[]*)&val;
- }
- }
- else
- {
- static inout(void)[] _aaValues(inout void* aa)
- {
- return [];
- }
- }
- static if (isCopyConstructable!Key)
- {
- static inout(void)[] _aaKeys(inout void* aa)
- {
- auto _this = *cast(AssociativeArray*)&aa;
- auto key = _this.keys;
- return *cast(inout(void)[]*)&key;
- }
- }
- else
- {
- static inout(void)[] _aaKeys(inout void* aa)
- {
- return [];
- }
- }
- static void* _aaRehash(void** paa) pure nothrow
- {
- assert(*paa);
- auto pthis = *cast(AssociativeArray**)&paa;
- auto _this = *pthis;
- _this.rehash();
- *pthis = _this;
- return *cast(void**)&_this;
- }
- static void* _aaDup(void* aa)
- {
- auto _this = *cast(AssociativeArray*)&aa;
- auto copy = _this.dup();
- return *cast(void**)©
- }
- static AARange _aaRange(void* aa) pure nothrow @nogc
- {
- auto _this = *cast(AssociativeArray*)&aa;
- return _this.getRange!(KeyValue.Unknown)().toAARange();
- }
- static bool _aaRangeEmpty(AARange r) pure nothrow @nogc
- {
- return Range!(Mutability.Mutable).fromAARange(r).empty;
- }
- static void* _aaRangeFrontKey(AARange r) pure nothrow @nogc
- {
- auto _range = Range!(Mutability.Mutable).fromAARange(r);
- return cast(void*)&_range.front!(KeyValue.Key)();
- }
- static void* _aaRangeFrontValue(AARange r) pure nothrow @nogc
- {
- auto _range = Range!(Mutability.Mutable).fromAARange(r);
- return cast(void*)&_range.front!(KeyValue.Value)();
- }
- static void _aaRangePopFront(ref AARange r) pure nothrow @nogc
- {
- auto _range = Range!(Mutability.Mutable).fromAARange(r);
- _range.popFront();
- r = _range.toAARange();
- }
- }
- /***************************************************************
- compiler interface
- ***************************************************************/
- auto aaLiteral(Key, Value, T...)(ref T args)
- {
- static if (T.length)
- {
- auto aa = AssociativeArray!(Key, Value)(args);
- return aa.impl;
- }
- else
- {
- return AssociativeArray!(Key, Value).newImpl();
- }
- }
- size_t getMaxDepth(Key, Value)(ref AssociativeArray!(Key, Value) aa)
- {
- size_t max_depth = 0;
- for (size_t i = 0; i < aa.impl.buckets.length; i++)
- {
- if (max_depth < aa.impl.buckets[i].depth)
- max_depth = aa.impl.buckets[i].depth;
- }
- return max_depth;
- }
- extern (D) alias int delegate(void *) dg_t;
- extern (D) alias int delegate(void *, void *) dg2_t;
- extern (D) alias void* function() init_t;
- //!Note: this functions are extern(D) now and doesn't conflict with rt.aaA _aaXXX functions
- extern(C)
- {
- size_t _aaLen(in void* aa) @trusted pure nothrow
- {
- if (!aa)
- return 0;
- auto handle = *cast(AssociativeArrayHandle**)aa;
- return handle.len(aa);
- }
- size_t _aaGetHash(in void* aa)
- {
- if (!aa)
- return 0;
- auto handle = *cast(AssociativeArrayHandle**)aa;
- return handle.getHash(aa);
- }
- void* _aaGetX(void** aa, const TypeInfo ti, in size_t, in void* pkey, init_t init) @trusted
- in
- {
- assert(aa);
- }
- body
- {
- if (!*aa)
- {
- *aa = init();
- }
- auto handle = *cast(AssociativeArrayHandle**)*aa;
- return handle.getX(aa, pkey);
- }
- inout(void)* _aaGetRvalueX(inout void* aa, const TypeInfo, in size_t, in void* pkey) @trusted
- {
- if (!aa)
- return null;
- auto handle = *cast(AssociativeArrayHandle**)aa;
- return handle.getRvalueX(aa, pkey);
- }
- inout(void)* _aaInX(inout void* aa, in TypeInfo unused_1, in void* pkey) @trusted
- in
- {
- assert(aa);
- }
- body
- {
- return _aaGetRvalueX(aa, unused_1, 0, pkey);
- }
- bool _aaDelX(void* aa, in TypeInfo, in void* pkey) @trusted
- {
- if (!aa)
- return false;
- auto handle = *cast(AssociativeArrayHandle**)aa;
- return handle.delX(aa, pkey);
- }
- int _aaApply(void* aa, in size_t keysize, dg_t dg)
- {
- if (!aa)
- return 0;
- auto handle = *cast(AssociativeArrayHandle**)aa;
- return handle.apply(aa, dg);
- }
- int _aaApply2(void* aa, in size_t keysize, dg2_t dg)
- {
- if (!aa)
- return 0;
- auto handle = *cast(AssociativeArrayHandle**)aa;
- return handle.apply2(aa, dg);
- }
- int _aaEqual(in TypeInfo, in void* e1, in void* e2) @trusted
- in
- {
- if (e1 && e2)
- {
- auto handle1 = *cast(AssociativeArrayHandle**)e1;
- auto handle2 = *cast(AssociativeArrayHandle**)e2;
- assert(handle1 == handle2); //ensure that both objects has a same type
- }
- }
- body
- {
- if (!e1 && !e2)
- {
- return true;
- }
- if (e1 && e2)
- {
- auto handle = *cast(AssociativeArrayHandle**)e1;
- return handle.equal(e1, e2);
- }
- return false;
- }
- inout(void)[] _aaValues(inout void* aa, in size_t keysize, in size_t valuesize)
- {
- if (!aa)
- return [];
- auto handle = *cast(AssociativeArrayHandle**)aa;
- return handle.values(aa);
- }
- inout(void)[] _aaKeys(inout void* aa, in size_t keysize)
- {
- if (!aa)
- return [];
- auto handle = *cast(AssociativeArrayHandle**)aa;
- return handle.keys(aa);
- }
- void* _aaRehash(void** paa, in TypeInfo keyti) pure nothrow
- in
- {
- assert(paa);
- }
- body
- {
- auto aa = *paa;
- if (!aa) return null;
- auto handle = *cast(AssociativeArrayHandle**)aa;
- return handle.rehash(paa);
- }
- void* _aaDup(void* aa, in TypeInfo keyti)
- {
- if (!aa)
- return null;
- auto handle = *cast(AssociativeArrayHandle**)aa;
- return handle.dup(aa);
- }
- struct AARange
- {
- void* impl;
- void* current;
- }
- AARange _aaRange(void* aa) pure nothrow @nogc
- {
- if (!aa)
- {
- return AARange(null, null);
- }
- auto handle = *cast(AssociativeArrayHandle**)aa;
- return handle.range(aa);
- }
- bool _aaRangeEmpty(AARange r) pure nothrow @nogc
- {
- auto aa = r.impl;
- if (!aa)
- {
- return true;
- }
- auto handle = *cast(AssociativeArrayHandle**)aa;
- return handle.rangeEmpty(r);
- }
- void* _aaRangeFrontKey(AARange r) pure nothrow @nogc
- in
- {
- assert(r.impl && r.current);
- }
- body
- {
- auto aa = r.impl;
- auto handle = *cast(AssociativeArrayHandle**)aa;
- return handle.rangeFrontKey(r);
- }
- void* _aaRangeFrontValue(AARange r) pure nothrow @nogc
- in
- {
- assert(r.impl && r.current);
- }
- body
- {
- auto aa = r.impl;
- auto handle = *cast(AssociativeArrayHandle**)aa;
- return handle.rangeFrontValue(r);
- }
- void _aaRangePopFront(ref AARange r) pure nothrow @nogc
- {
- if (!r.impl)
- return;
- auto aa = r.impl;
- auto handle = *cast(AssociativeArrayHandle**)aa;
- return handle.rangePopFront(r);
- }
- }
- version(unittest)
- {
- int test1(bool isstatic)()
- {
- static if (isstatic)
- static aa1 = AssociativeArray!(real, real)(1,2.0, 3.9L,4UL, 5UL,6);
- else
- auto aa1 = AssociativeArray!(real, real)(1,2.0, 3.9L,4UL, 5UL,6);
- assert(aa1[1] == 2);
- assert(aa1[3.9] == 4);
- assert(aa1[5] == 6);
- assert(aa1.length == 3);
- aa1[2] = 3;
- aa1[3.9] = 8;
- aa1[5] += 4;
- aa1[8] = 8;
- aa1[9] = 9;
- aa1[10] = 10;
- assert(aa1[1] == 2);
- assert(aa1[2] == 3);
- assert(aa1[3.9] == 8);
- assert(aa1[5] == 10);
- assert(aa1[8] == 8);
- assert(aa1[9] == 9);
- assert(aa1[10] == 10);
- assert(aa1.length == 7);
- aa1 = aa1.rehash();
- assert(aa1[1] == 2);
- assert(aa1[2] == 3);
- assert(aa1[3.9] == 8);
- assert(aa1[5] == 10);
- assert(aa1[8] == 8);
- assert(aa1[9] == 9);
- assert(aa1[10] == 10);
- assert(aa1.length == 7);
- auto aa2 = aa1.dup;
- assert(aa2[1] == 2);
- assert(aa2[2] == 3);
- assert(aa2[3.9] == 8);
- assert(aa2[5] == 10);
- assert(aa2[8] == 8);
- assert(aa2[9] == 9);
- assert(aa2[10] == 10);
- assert(aa2.length == 7);
- assert(aa1 == aa2);
- aa2[99] = 99;
- assert(aa2.length == 8);
- assert(aa1 != aa2);
- return 0;
- }
- int test2()
- {
- auto aa1 = AssociativeArray!(int, int)(1,2, 3,4, 5,6);
- //test length
- assert(_aaLen(*cast(void**)&aa1));
- int newkey = 7;
- //create a new elem
- auto pval = _aaGetX(cast(void**)&aa1, null, 0, &newkey, &aaLiteral!(int, int));
- *cast(int*)pval = 8;
- assert(aa1[7] == 8);
- newkey = 1;
- //find the existing elem
- pval = _aaGetX(cast(void**)&aa1, null, 0, &newkey, &aaLiteral!(int, int));
- *cast(int*)pval = 1;
- assert(aa1[1] == 1);
- AssociativeArray!(int, int) aa2;
- auto impl1 = aa2.impl;
- newkey = 1;
- //create a new elem in aa with null impl
- pval = _aaGetX(cast(void**)&aa2, null, 0, &newkey, &aaLiteral!(int, int));
- *cast(int*)pval = 1;
- auto impl2 = aa2.impl;
- assert(impl2 != impl1);
- assert(aa2[1] == 1);
- //find the existing elem
- int key = 3;
- auto rval1 = _aaGetRvalueX(*cast(void**)&aa1, null, 0, &key);
- auto rval2 = _aaInX(*cast(void**)&aa1, null, &key);
- assert(rval1 == rval2 && *cast(int*)rval1 == 4);
- //find non-existing elem
- key = 4;
- rval1 = _aaGetRvalueX(*cast(void**)&aa1, null, 0, &key);
- rval2 = _aaInX(*cast(void**)&aa1, null, &key);
- assert(!rval1 && !rval2);
- //delete non-existing elem
- auto rem = _aaDelX(*cast(void**)&aa1, null, &key);
- assert(!rem);
- //delete the existing elem
- key = 3;
- rem = _aaDelX(*cast(void**)&aa1, null, &key);
- assert(rem);
- assert(_aaLen(*cast(void**)&aa1) == 3);
- //test _aaApply and _aaApply2
- int sum = 0;
- int dg1(void* v)
- {
- sum += *cast(int*)v;
- return 0;
- }
- _aaApply(*cast(void**)&aa1, 0, &dg1);
- assert(sum == 15);
- sum = 0;
- int dg2(void* k, void* v)
- {
- sum += *cast(int*)k;
- sum += *cast(int*)v;
- return 0;
- }
- _aaApply2(*cast(void**)&aa1, 0, &dg2);
- assert(sum == 28);
- //test _aaEqual
- auto aa3 = aa1.dup;
- assert(_aaEqual(null, *cast(void**)&aa1, *cast(void**)&aa3));
- int k1 = 1, k2 = 3, v1 = 2, v2 = 4;
- auto aalit = aaLiteral!(int, int)(k1, v1, k2, v2);
- return 0;
- }
- int test3()
- {
- AssociativeArray!(int, int) aa1;
- auto impl1 = aa1.impl;
- assert(aa1.length == 0);
- assert(aa1.keys.length == 0);
- assert(aa1.values.length == 0);
- assert(aa1.byKey().empty);
- assert(aa1.byValue().empty);
- foreach (cur; aa1)
- {
- assert(0);
- }
- foreach (key, val; aa1)
- {
- assert(0);
- }
- auto aa2 = aa1.dup;
- assert(aa2 == aa1);
- aa1.rehash;
- assert(aa1.impl is impl1);
- AssociativeArray!(int, int) aa3;
- auto impl2 = aa3.impl;
- assert(impl1 is impl2);
- aa3[1] = 2;
- assert(aa3.impl !is impl2);
- AssociativeArray!(int, int) aa4;
- assert(aa4.impl is impl1);
- aa1 = aa2;
- assert(aa1.impl == aa2.impl);
- aa1 = null;
- assert(aa1.impl == impl1);
- return 0;
- }
- int test4(bool isstatic)()
- {
- static if (isstatic)
- static aa1 = AssociativeArray!(int, int)(1,2, 3,4, 5,6);
- else
- auto aa1 = AssociativeArray!(int, int)(1,2, 3,4, 5,6);
- assert(aa1[1] == 2);
- assert(aa1.length == 3);
- auto aa2 = aa1.dup;
- aa2[1] = 8; //Mutable copy
- int sum = 0;
- foreach (key, val; aa1)
- {
- sum += key;
- sum += val;
- }
- assert(sum == 21);
- sum = 0;
- foreach (cur; aa1.byKey())
- {
- sum += cur;
- }
- assert(sum == 9);
- sum = 0;
- foreach (cur; aa1.keys)
- {
- sum += cur;
- }
- assert(sum == 9);
- sum = 0;
- foreach (cur; aa1.byValue())
- {
- sum += cur;
- }
- assert(sum == 12);
- sum = 0;
- foreach (cur; aa1.values)
- {
- sum += cur;
- }
- assert(sum == 12);
- sum = 0;
- foreach (cur; aa1)
- {
- sum += cur;
- }
- assert(sum == 12);
- assert(aa1.get(3, 42) == 4);
- assert(aa1.get(7, 42) == 42);
- auto val = 5 in aa1;
- assert(val && *val == 6);
- return 0;
- }
- unittest
- {
- auto runtime1 = test1!false();
- auto runtime1_1 = test1!true();
- enum compiletime1 = test1!false();
- auto runtime2 = test2();
- auto runtime3 = test3();
- enum compiletime3 = test3();
- auto runtime4 = test4!(false)();
- auto runtime4_1 = test4!(true)();
- enum compiletime4 = test4!(false)();
- }
- }
- private enum OpEqualsArg
- {
- Const,
- Mutable
- }
- private template lvlOpEqualsArgModififer(T)
- {
- static if(is(typeof(const(T).init == T.init)))
- {
- enum lvlOpEqualsArgModififer = OpEqualsArg.Const;
- }
- else
- {
- enum lvlOpEqualsArgModififer = OpEqualsArg.Mutable;
- }
- }
- private template rvlOpEqualsArgModififer(T)
- {
- static if(is(typeof(T.init == const(T).init)))
- {
- enum rvlOpEqualsArgModififer = OpEqualsArg.Const;
- }
- else
- {
- enum rvlOpEqualsArgModififer = OpEqualsArg.Mutable;
- }
- }
- private void _emplace(bool new_obj, V1, V2)(ref V1 dst, ref V2 src) @trusted if (is(V1 == struct))
- {
- static if (new_obj)
- {
- (cast(void*)&dst)[0 .. V1.sizeof] = (cast(void*)&src)[0 .. V1.sizeof]; //bitwise copy of object, which already postblitted
- _postblit(dst);
- }
- else
- {
- V1 tmp2 = void;
- (cast(void*)&tmp2)[0 .. V1.sizeof] = (cast(void*)dst)[0 .. V1.sizeof];
- (cast(void*)&dst)[0 .. V1.sizeof] = (cast(void*)&src)[0 .. V1.sizeof];
- _postblit(dst);
- //Now tmp2 contains the old dst value (and it will be destructed)
- }
- }
- private void _emplace(bool new_obj, V1, V2)(ref V1 dst, ref V2 src) @trusted if (__traits(isStaticArray, V1))
- {
- static assert(V1.sizeof == V2.sizeof);
- foreach(i; 0 .. dst.length)
- {
- _emplace!(new_obj)(dst[i], src[i]);
- }
- }
- private void _emplace(bool new_obj, V1, V2)(ref V1 dst, ref V2 src) @trusted if (!__traits(isStaticArray, V1) && !is(V1 == struct))
- {
- static assert(V1.sizeof == V2.sizeof);
- *(cast(Unqual!V1*)&dst) = *(cast(Unqual!V1*)&src);
- }
- private void _dispose(V1)(ref V1 dst) @trusted if (is(V1 == struct))
- {
- V1 tmp = void;
- V1 init = V1.init;
- (cast(void*)&tmp)[0 .. V1.sizeof] = (cast(void*)dst)[0 .. V1.sizeof];
- (cast(void*)&dst)[0 .. V1.sizeof] = (cast(void*)&init)[0 .. V1.sizeof];
- }
- private void _dispose(V1)(ref V1 dst) @trusted if (__traits(isStaticArray, V1))
- {
- foreach(i; 0 .. dst.length)
- {
- _dispose(dst[i]);
- }
- }
- private void _dispose(V1)(ref V1 dst) @trusted if (!__traits(isStaticArray, V1) && !is(V1 == struct))
- {
- V1 init;
- *(cast(Unqual!V1*)&dst) = *(cast(Unqual!V1*)&tmp);
- }
- private void _postblit(V1)(ref V1 obj)
- {
- static if (__traits(isStaticArray, V1))
- {
- foreach(i; 0 .. obj.length)
- {
- _postblit(obj[i]);
- }
- }
- else static if(is(V1 == struct) && !__traits(isPOD, V1))
- {
- foreach(i, ref _; obj.tupleof)
- {
- _postblit(obj.tupleof[i]);
- }
- static if (__traits(hasMember, obj, "__postblit"))
- {
- obj.__postblit();
- }
- }
- }
- private template isCopyConstructable(T)
- {
- static if (is(typeof({
- T v;
- T v2 = v;
- })))
- {
- enum isCopyConstructable = true;
- }
- else
- {
- enum isCopyConstructable = false;
- }
- }
- private template isAssignable(V1, V2)
- {
- static if (__traits(compiles, {V1 val = V1.init; V2 val2 = V2.init; val = val2;}))
- {
- enum isAssignable = true;
- }
- else
- {
- enum isAssignable = false;
- }
- }
- private enum KeyValue
- {
- Key = 0,
- Value = 1,
- Unknown = 2
- }
- private template commonType(KeyValue kv, T...)
- {
- static assert(T.length && T.length % 2 == 0);
- static if (T.length == 2)
- {
- alias commonType = T[kv];
- }
- else
- {
- alias commonType = typeof(true ? T[kv].init : commonType!(kv, T[2 .. $]).init);
- }
- }
- private struct AssociativeArrayHandle
- {
- @trusted pure nothrow size_t function(in void* aa) len;
- @trusted void* function(void** aa, in void* pkey) getX;
- @trusted inout(void)* function(inout void* aa, in void* pkey) getRvalueX;
- @trusted bool function(void* aa, in void* pkey) delX;
- int function(void* aa, dg_t dg) apply;
- int function(void* aa, dg2_t dg) apply2;
- @trusted int function(in void* e1, in void* e2) equal;
- inout(void)[] function(inout void* p) values;
- inout(void)[] function(inout void* p) keys;
- pure nothrow void* function(void** pp) rehash;
- void* function(void* pp) dup;
- @nogc pure nothrow AARange function(void* aa) range;
- @nogc pure nothrow bool function(AARange r) rangeEmpty;
- @nogc pure nothrow void* function(AARange r) rangeFrontKey;
- @nogc pure nothrow void* function(AARange r) rangeFrontValue;
- @nogc pure nothrow void function(ref AARange r) rangePopFront;
- size_t function(in void* aa) getHash;
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement