381 lines
11 KiB
C#
381 lines
11 KiB
C#
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<TPrimaryKey, TSubKey, TValue>
|
|
where TPrimaryKey : notnull
|
|
where TSubKey : notnull
|
|
{
|
|
private readonly ConcurrentDictionary<TPrimaryKey, TValue> m_values_by_primary_key = new();
|
|
private readonly ConcurrentDictionary<TSubKey, TPrimaryKey> m_sub_key_to_primary_keys = new();
|
|
private readonly ConcurrentDictionary<TPrimaryKey, TSubKey> 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<TValue> 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<KeyValuePair<TPrimaryKey, TValue>> GetEnumerator()
|
|
{
|
|
return m_values_by_primary_key.GetEnumerator();
|
|
}
|
|
}
|