using Newtonsoft.Json.Linq; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ServerCore; // HANDOVER: 다중 Key로 특정 정보를 관리할 수 있는 색인 자료 구조를 제공 한다. public class MultiIndexDictionary where TPrimaryKey : notnull where TSubKey : notnull { private readonly ConcurrentDictionary m_values_by_primary_key = new(); private readonly ConcurrentDictionary m_sub_key_to_primary_keys = new(); private readonly ConcurrentDictionary m_primary_key_to_sub_keys = new(); private object m_lock = new(); public TValue this[TSubKey subKey] { get { TValue? item; if (tryGetValue(subKey, out item)) { ArgumentNullException.ThrowIfNull(item, $"item is null !!!"); return item; } throw new KeyNotFoundException($"Not found SubKey !!! : {subKey.ToString()}"); } } public TValue this[TPrimaryKey primaryKey] { get { TValue? item; if (tryGetValue(primaryKey, out item)) { ArgumentNullException.ThrowIfNull(item, $"item is null !!!"); return item; } throw new KeyNotFoundException($"Not found PrimaryKey !!! : {primaryKey.ToString()}"); } } public bool trySubKeyBindToPrimaryKey(TSubKey subKey, TPrimaryKey primaryKey) { var is_success = true; try { lock(m_lock) { if (false == m_values_by_primary_key.ContainsKey(primaryKey)) { throw new KeyNotFoundException($"Not found PrimaryKey !!! : {primaryKey.ToString()}"); } if (m_primary_key_to_sub_keys.ContainsKey(primaryKey)) { try { if (m_sub_key_to_primary_keys.ContainsKey(m_primary_key_to_sub_keys[primaryKey])) { m_sub_key_to_primary_keys.TryRemove(m_primary_key_to_sub_keys[primaryKey], out _); } m_primary_key_to_sub_keys.TryRemove(primaryKey, out _); } catch (Exception e) { is_success = false; var err_msg = $"Exception !!! MultiIndexDictionary.trySubKeyBindToPrimaryKey(), bound Key remove !!! :{primaryKey.ToString()}, exception:{e}"; Log.getLogger().error(err_msg); } } m_sub_key_to_primary_keys[subKey] = primaryKey; m_primary_key_to_sub_keys[primaryKey] = subKey; } } catch(Exception e) { is_success = false; var err_msg = $"Exception !!! MultiIndexDictionary.trySubKeyBindToPrimaryKey(), Not found PrimaryKey :{primaryKey.ToString()}, exception:{e}"; Log.getLogger().error(err_msg); } return is_success; } public bool tryGetSubValue(TSubKey subKey, [MaybeNullWhen(false)] out TValue val) { val = default(TValue); TPrimaryKey? primaryKey; if (m_sub_key_to_primary_keys.TryGetValue(subKey, out primaryKey)) { ArgumentNullException.ThrowIfNull(primaryKey, $"primaryKey is null !!!"); return m_values_by_primary_key.TryGetValue(primaryKey, out val); } return false; } public bool tryGetValue(TSubKey subKey, out TValue? val) { val = default(TValue); TPrimaryKey? primaryKey; if (m_sub_key_to_primary_keys.TryGetValue(subKey, out primaryKey)) { ArgumentNullException.ThrowIfNull(primaryKey, $"primaryKey is null !!!"); return m_values_by_primary_key.TryGetValue(primaryKey, out val); } return false; } public bool tryGetPrimaryValue(TPrimaryKey primaryKey, [MaybeNullWhen(false)] out TValue val) { return m_values_by_primary_key.TryGetValue(primaryKey, out val); } public bool tryGetValue(TPrimaryKey primaryKey, [MaybeNullWhen(false)] out TValue val) { return m_values_by_primary_key.TryGetValue(primaryKey, out val); } public bool containsSubKey(TSubKey subKey) { TValue? val; return tryGetSubValue(subKey, out val); } public bool containsPrimaryKey(TPrimaryKey primaryKey) { TValue? val; return tryGetPrimaryValue(primaryKey, out val); } public bool tryRemoveByPrimaryKey(TPrimaryKey primaryKey, out TValue? removedValue) { removedValue = default; bool is_success = true; try { lock (m_lock) { if (m_primary_key_to_sub_keys.ContainsKey(primaryKey)) { if (m_sub_key_to_primary_keys.ContainsKey(m_primary_key_to_sub_keys[primaryKey])) { m_sub_key_to_primary_keys.TryRemove(m_primary_key_to_sub_keys[primaryKey], out _); } m_primary_key_to_sub_keys.TryRemove(primaryKey, out _); } if (false == m_values_by_primary_key.TryRemove(primaryKey, out removedValue)) { is_success = false; var err_msg = $"Failed to TryRemove() !!!, SubKey:{primaryKey.ToString()}"; Log.getLogger().debug(err_msg); } } } catch (Exception e) { is_success = false; var err_msg = $"Exception !!! MultiIndexDictionary.remove() !!!, PrimaryKey:{primaryKey.ToString()}, exception:{e}"; Log.getLogger().error(err_msg); } return is_success; } public bool tryRemoveBySubKey(TSubKey subKey, out TValue? removedValue) { removedValue = default; bool is_success = true; try { lock (m_lock) { if (false == m_values_by_primary_key.TryRemove(m_sub_key_to_primary_keys[subKey], out removedValue)) { is_success = false; var err_msg = $"Failed to TryRemove() !!!, SubKey:{subKey.ToString()}"; Log.getLogger().error(err_msg); } else { m_primary_key_to_sub_keys.TryRemove(m_sub_key_to_primary_keys[subKey], out _); m_sub_key_to_primary_keys.TryRemove(subKey, out _); } } } catch (Exception e) { is_success = false; var err_msg = $"Exception !!! MultiIndexDictionary.remove() !!!, SubKey:{subKey.ToString()}, exception:{e}"; Log.getLogger().error(err_msg); } return is_success; } public bool tryReplaceSubKey(TSubKey newSubKey, TPrimaryKey targetPrimaryKey) { bool is_success = true; try { lock(m_lock) { if (true == m_sub_key_to_primary_keys.ContainsKey(newSubKey)) { is_success = false; var err_msg = $"Failed to ContainsKey() !!!, Already contained SubKey !!! : NewSubKey:{newSubKey.ToString()} - TargetPrimaryKey:{targetPrimaryKey.ToString()}"; Log.getLogger().error(err_msg); } else { if (false == m_primary_key_to_sub_keys.TryRemove(targetPrimaryKey, out var target_sub_key)) { is_success = false; var err_msg = $"Failed to TryRemove() !!!, TargetPrimaryKey:{targetPrimaryKey.ToString()}, NewSubKey:{newSubKey.ToString()}"; Log.getLogger().error(err_msg); } else { if (false == m_sub_key_to_primary_keys.TryRemove(target_sub_key, out _)) { is_success = false; var err_msg = $"Failed to TryRemove() !!!, TargetSubKey:{target_sub_key.ToString()}, PrimaryKey:{targetPrimaryKey.ToString()}"; Log.getLogger().error(err_msg); } else { m_sub_key_to_primary_keys[newSubKey] = targetPrimaryKey; } } } } } catch (Exception e) { is_success = false; var err_msg = $"Exception !!! MultiIndexDictionary.tryReplaceSubKey() !!! : Exception:{e} - NewSubKey:{newSubKey.ToString()}, TargetPrimaryKey:{targetPrimaryKey.ToString()}"; Log.getLogger().error(err_msg); } return is_success; } public bool tryAdd(TPrimaryKey primaryKey, TValue val) { bool is_success = true; try { if(false == m_values_by_primary_key.TryAdd(primaryKey, val)) { is_success = false; var err_msg = $"Failed to TryAdd() !!!, PrimaryKey:{primaryKey.ToString()}"; Log.getLogger().error(err_msg); } } catch(Exception e) { is_success = false; var err_msg = $"Exception !!! MultiIndexDictionary.add() !!!, PrimaryKey:{primaryKey.ToString()}, exception:{e}"; Log.getLogger().error(err_msg); } return is_success; } public bool tryAdd(TPrimaryKey primaryKey, TSubKey subKey, TValue val) { var is_success = tryAdd(primaryKey, val); if(false == is_success) { return false; } return trySubKeyBindToPrimaryKey(subKey, primaryKey); } public TValue[] cloneValues() { var values = new TValue[m_values_by_primary_key.Values.Count]; m_values_by_primary_key.Values.CopyTo(values, 0); return values; } public List Values { get { return m_values_by_primary_key.Values.ToList(); } } public TPrimaryKey[] clonePrimaryKeys() { var primary_keys = new TPrimaryKey[m_values_by_primary_key.Keys.Count]; m_values_by_primary_key.Keys.CopyTo(primary_keys, 0); return primary_keys; } public TSubKey[] cloneSubKeys() { var sub_keys = new TSubKey[m_sub_key_to_primary_keys.Keys.Count]; m_sub_key_to_primary_keys.Keys.CopyTo(sub_keys, 0); return sub_keys; } public void clear() { m_values_by_primary_key.Clear(); m_sub_key_to_primary_keys.Clear(); m_primary_key_to_sub_keys.Clear(); } public int Count { get { return m_values_by_primary_key.Count; } } public IEnumerator> GetEnumerator() { return m_values_by_primary_key.GetEnumerator(); } }