초기커밋
This commit is contained in:
376
ServerCore/Container/MultiIndexDictionary.cs
Normal file
376
ServerCore/Container/MultiIndexDictionary.cs
Normal file
@@ -0,0 +1,376 @@
|
||||
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;
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
150
ServerCore/Container/PriorityQueue.cs
Normal file
150
ServerCore/Container/PriorityQueue.cs
Normal file
@@ -0,0 +1,150 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
||||
|
||||
|
||||
namespace ServerCore;
|
||||
|
||||
//=============================================================================================
|
||||
// 우선 순위 정책에 의해 순서를 정렬해주는 컨테이너 클래스 이다.
|
||||
//
|
||||
// author : kangms
|
||||
//
|
||||
// From http://visualstudiomagazine.com/articles/2012/11/01/priority-queues-with-c.aspx
|
||||
//=============================================================================================
|
||||
|
||||
public class PriorityQueue<T> : IEnumerable<T> where T : IComparable<T>
|
||||
{
|
||||
protected List<T> m_datas;
|
||||
|
||||
public PriorityQueue()
|
||||
{
|
||||
m_datas = new();
|
||||
}
|
||||
|
||||
// 큐에 새 데이터 추가
|
||||
public void enqueue(T item)
|
||||
{
|
||||
m_datas.Add(item);
|
||||
var child_index = m_datas.Count - 1;
|
||||
|
||||
// binary heaps algorithm을 이용한 정렬
|
||||
while (child_index > 0)
|
||||
{
|
||||
var parent_index = (child_index - 1) / 2;
|
||||
if (m_datas[child_index].CompareTo(m_datas[parent_index]) >= 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
T tmp = m_datas[child_index];
|
||||
m_datas[child_index] = m_datas[parent_index];
|
||||
m_datas[parent_index] = tmp;
|
||||
child_index = parent_index;
|
||||
}
|
||||
}
|
||||
|
||||
// 최상단 pop
|
||||
public T dequeue()
|
||||
{
|
||||
var last_index = m_datas.Count - 1;
|
||||
|
||||
T frontItem = m_datas[0]; // fetch the front
|
||||
m_datas[0] = m_datas[last_index];
|
||||
m_datas.RemoveAt(last_index);
|
||||
|
||||
--last_index; // last index (after removal)
|
||||
var parent_index = 0; // parent index. start at front of pq
|
||||
// binary heaps algorithm을 이용한 정렬
|
||||
while (true)
|
||||
{
|
||||
var client_index = parent_index * 2 + 1; // left child index of parent
|
||||
if (client_index > last_index)
|
||||
{
|
||||
break; // no children so done
|
||||
}
|
||||
|
||||
var rc = client_index + 1; // right child
|
||||
if (rc <= last_index && m_datas[rc].CompareTo(m_datas[client_index]) < 0) // if there is a rc (ci + 1), and it is smaller than left child, use the rc instead
|
||||
{
|
||||
client_index = rc;
|
||||
}
|
||||
|
||||
if (m_datas[parent_index].CompareTo(m_datas[client_index]) <= 0)
|
||||
{
|
||||
break; // parent is smaller than (or equal to) smallest child so done
|
||||
}
|
||||
|
||||
T tmp = m_datas[parent_index];
|
||||
m_datas[parent_index] = m_datas[client_index];
|
||||
m_datas[client_index] = tmp; // swap parent and child
|
||||
parent_index = client_index;
|
||||
}
|
||||
|
||||
return frontItem;
|
||||
}
|
||||
|
||||
// 최상단 얻기
|
||||
public T peek()
|
||||
{
|
||||
T frontItem = m_datas[0];
|
||||
return frontItem;
|
||||
}
|
||||
|
||||
public Int32 count()
|
||||
{
|
||||
return m_datas.Count;
|
||||
}
|
||||
|
||||
public virtual string toBasicString()
|
||||
{
|
||||
string str = "";
|
||||
for (Int32 index = 0; index < m_datas.Count; ++index)
|
||||
{
|
||||
str += m_datas[index].ToString() + " ";
|
||||
}
|
||||
str += "count = " + m_datas.Count;
|
||||
return str;
|
||||
}
|
||||
|
||||
//
|
||||
public bool isConsistent()
|
||||
{
|
||||
// is the heap property true for all data?
|
||||
if (m_datas.Count == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var li = m_datas.Count - 1; // last index
|
||||
for (var parent_index = 0; parent_index < m_datas.Count; ++parent_index)
|
||||
{ // each parent index
|
||||
var left_child_index = 2 * parent_index + 1; // left child index
|
||||
var right_child_index = 2 * parent_index + 2; // right child index
|
||||
|
||||
if (left_child_index <= li && m_datas[parent_index].CompareTo(m_datas[left_child_index]) > 0)
|
||||
{
|
||||
return false; // if lc exists and it's greater than parent then bad.
|
||||
}
|
||||
if (right_child_index <= li && m_datas[parent_index].CompareTo(m_datas[right_child_index]) > 0)
|
||||
{
|
||||
return false; // check the right child too.
|
||||
}
|
||||
}
|
||||
return true; // passed all checks
|
||||
}
|
||||
|
||||
public IEnumerator<T> GetEnumerator()
|
||||
{
|
||||
return m_datas.GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user