|
@@ -0,0 +1,183 @@
|
|
|
|
|
+using Invercargill.DataStructures;
|
|
|
|
|
+
|
|
|
|
|
+namespace Invercargill {
|
|
|
|
|
+
|
|
|
|
|
+ public class Catalogue<TKey, TValue> : Enumerable<Grouping<TKey, TValue>>, Lot<Grouping<TKey, TValue>>, ReadOnlyCategoryIndex<TKey, TValue>, CategoryIndex<TKey, TValue> {
|
|
|
|
|
+
|
|
|
|
|
+ public Enumerable<TKey> keys { owned get { return lookup.keys; } }
|
|
|
|
|
+ public Enumerable<TValue> values { owned get { return lookup.values.select_many<TValue>(g => g); } }
|
|
|
|
|
+ public uint length { get {
|
|
|
|
|
+ rw_lock.reader_lock ();
|
|
|
|
|
+ var count = item_count;
|
|
|
|
|
+ rw_lock.reader_unlock ();
|
|
|
|
|
+ return count;
|
|
|
|
|
+ }}
|
|
|
|
|
+
|
|
|
|
|
+ private Dictionary<TKey, Series<TValue>> lookup;
|
|
|
|
|
+ private uint item_count;
|
|
|
|
|
+ private RWLock rw_lock;
|
|
|
|
|
+
|
|
|
|
|
+ public Catalogue(HashDelegate<TKey>? key_hash_func = null, EqualityDelegate<TKey>? key_equal_func = null) {
|
|
|
|
|
+ lookup = new Dictionary<TKey, Series<TValue>> (key_hash_func, key_equal_func);
|
|
|
|
|
+ rw_lock = RWLock();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public override Tracker<Grouping<TKey, TValue>> get_tracker () {
|
|
|
|
|
+ rw_lock.reader_lock ();
|
|
|
|
|
+ var result = lookup
|
|
|
|
|
+ .select<Grouping<TKey, TValue>>(kv => new Grouping<TKey, TValue>(kv.key, kv.value))
|
|
|
|
|
+ .get_tracker();;
|
|
|
|
|
+ rw_lock.reader_unlock ();
|
|
|
|
|
+ return result;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public override uint? peek_count () {
|
|
|
|
|
+ return item_count;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public override EnumerableInfo get_info () {
|
|
|
|
|
+ return new EnumerableInfo.infer_ultimate (this, EnumerableCategory.IN_MEMORY);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public bool try_get (TKey key, out Grouping<TKey, TValue> value) {
|
|
|
|
|
+ rw_lock.reader_lock();
|
|
|
|
|
+ Series<TValue> series;
|
|
|
|
|
+ if(lookup.try_get (key, out series)) {
|
|
|
|
|
+ value = new Grouping<TKey, TValue>(key, series);
|
|
|
|
|
+ rw_lock.reader_unlock();
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+ rw_lock.reader_unlock();
|
|
|
|
|
+ value = null;
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public bool has (TKey key) {
|
|
|
|
|
+ rw_lock.reader_lock();
|
|
|
|
|
+ var result = lookup.has(key);
|
|
|
|
|
+ rw_lock.reader_unlock();
|
|
|
|
|
+ return result;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public bool try_get_any (TKey key, out TValue value) {
|
|
|
|
|
+ rw_lock.reader_lock();
|
|
|
|
|
+ Series<TValue> series;
|
|
|
|
|
+ if(lookup.try_get (key, out series) && series.length > 0) {
|
|
|
|
|
+ value = series.first_or_default();
|
|
|
|
|
+ rw_lock.reader_unlock();
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+ rw_lock.reader_unlock();
|
|
|
|
|
+ value = null;
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ public new void @set (TKey key, Enumerable<TValue> values) {
|
|
|
|
|
+ rw_lock.writer_lock();
|
|
|
|
|
+ Series<TValue> series;
|
|
|
|
|
+ if(lookup.try_get (key, out series)) {
|
|
|
|
|
+ item_count -= series.length;
|
|
|
|
|
+ }
|
|
|
|
|
+ series = values.to_series();
|
|
|
|
|
+ lookup[key] = series;
|
|
|
|
|
+ item_count += series.length;
|
|
|
|
|
+ rw_lock.writer_unlock();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public void add (TKey key, TValue value) {
|
|
|
|
|
+ rw_lock.writer_lock ();
|
|
|
|
|
+ Series<TValue> series;
|
|
|
|
|
+ if(!lookup.try_get (key, out series)) {
|
|
|
|
|
+ series = new Series<TValue>();
|
|
|
|
|
+ lookup[key] = series;
|
|
|
|
|
+ }
|
|
|
|
|
+ series.add(value);
|
|
|
|
|
+ item_count ++;
|
|
|
|
|
+ rw_lock.writer_unlock ();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public void add_all (TKey key, Enumerable<TValue> values) {
|
|
|
|
|
+ rw_lock.writer_lock ();
|
|
|
|
|
+ Series<TValue> series;
|
|
|
|
|
+ if(!lookup.try_get (key, out series)) {
|
|
|
|
|
+ series = new Series<TValue>();
|
|
|
|
|
+ lookup[key] = series;
|
|
|
|
|
+ }
|
|
|
|
|
+ series.add_all(values.act(i => item_count++));
|
|
|
|
|
+ rw_lock.writer_unlock ();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public void remove_from (TKey key, TValue item, Invercargill.EqualityDelegate<TValue>? equator = null) {
|
|
|
|
|
+ rw_lock.writer_lock ();
|
|
|
|
|
+ Series<TValue> series;
|
|
|
|
|
+ if(lookup.try_get (key, out series)) {
|
|
|
|
|
+ item_count -= series.length;
|
|
|
|
|
+ series.remove(item, equator);
|
|
|
|
|
+ item_count += series.length;
|
|
|
|
|
+ remove_if_empty(key, series);
|
|
|
|
|
+ }
|
|
|
|
|
+ rw_lock.writer_unlock ();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public void remove_all_from (TKey key, Enumerable<TValue> items, Invercargill.EqualityDelegate<TValue>? equator = null) {
|
|
|
|
|
+ rw_lock.writer_lock ();
|
|
|
|
|
+ Series<TValue> series;
|
|
|
|
|
+ if(lookup.try_get (key, out series)) {
|
|
|
|
|
+ item_count -= series.length;
|
|
|
|
|
+ series.remove_all(items, equator);
|
|
|
|
|
+ item_count += series.length;
|
|
|
|
|
+ remove_if_empty(key, series);
|
|
|
|
|
+ }
|
|
|
|
|
+ rw_lock.writer_unlock ();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public void remove (TValue item, Invercargill.EqualityDelegate<TValue>? equator = null) {
|
|
|
|
|
+ rw_lock.writer_lock ();
|
|
|
|
|
+ foreach (var kv in lookup) {
|
|
|
|
|
+ item_count -= kv.value.length;
|
|
|
|
|
+ kv.value.remove (item, equator);
|
|
|
|
|
+ item_count += kv.value.length;
|
|
|
|
|
+ remove_if_empty(kv.key, kv.value);
|
|
|
|
|
+ }
|
|
|
|
|
+ rw_lock.writer_unlock ();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public void remove_all (Enumerable<TValue> items, Invercargill.EqualityDelegate<TValue>? equator = null) {
|
|
|
|
|
+ rw_lock.writer_lock ();
|
|
|
|
|
+ foreach (var kv in lookup) {
|
|
|
|
|
+ item_count -= kv.value.length;
|
|
|
|
|
+ kv.value.remove_all (items, equator);
|
|
|
|
|
+ item_count += kv.value.length;
|
|
|
|
|
+ remove_if_empty(kv.key, kv.value);
|
|
|
|
|
+ }
|
|
|
|
|
+ rw_lock.writer_unlock ();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public bool clear_key (TKey key) {
|
|
|
|
|
+ rw_lock.writer_lock ();
|
|
|
|
|
+ Series<TValue> series;
|
|
|
|
|
+ var result = lookup.remove (key, out series);
|
|
|
|
|
+ if(result) {
|
|
|
|
|
+ item_count -= series.length;
|
|
|
|
|
+ }
|
|
|
|
|
+ rw_lock.writer_unlock ();
|
|
|
|
|
+ return result;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public void clear () {
|
|
|
|
|
+ rw_lock.writer_lock ();
|
|
|
|
|
+ lookup.clear();
|
|
|
|
|
+ item_count = 0;
|
|
|
|
|
+ rw_lock.writer_unlock ();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private void remove_if_empty(TKey key, Series<TValue> series) {
|
|
|
|
|
+ if(series.length == 0) {
|
|
|
|
|
+ lookup.remove(key);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+}
|