Skip to content

Instantly share code, notes, and snippets.

@unitycoder
Created October 3, 2018 06:26
Show Gist options
  • Save unitycoder/b45c7d5c015663a8cb07989c9dc54194 to your computer and use it in GitHub Desktop.
Save unitycoder/b45c7d5c015663a8cb07989c9dc54194 to your computer and use it in GitHub Desktop.
Unity ECS Stuff & Snippets
// https://forum.unity.com/threads/hybrid-native-container-nativehashmaplist-tkey-tvalue.564118/
//#define DEBUG_HML
using Unity.Collections;
using System;
namespace E7.Native
{
/// <summary>
/// A hybrid of NativeHashMap and NativeList. You can both use int indexer or use the key at the same time.
/// Consumes more memory and slightly costly remove operation.
/// </summary>
public struct NativeHashMapList<TKey, TValue> : IDisposable
where TKey : struct, IEquatable<TKey>
where TValue : struct
{
/// <summary>
/// Maps a hashed key to an index that then get the data from `NativeList`
/// </summary>
private NativeHashMap<TKey, int> hashMap;
/// <summary>
/// The data storage, allows us to use `int` to enumerate it.
/// </summary>
private NativeList<TValue> list;
/// <summary>
/// For backward referencing to the hash map when removing by index.
/// </summary>
private NativeList<TKey> keyList;
public NativeHashMapList(int capacity, Allocator label)
{
hashMap = new NativeHashMap<TKey, int>(capacity, label);
list = new NativeList<TValue>(capacity, label);
keyList = new NativeList<TKey>(capacity, label);
}
public void Dispose()
{
hashMap.Dispose();
list.Dispose();
keyList.Dispose();
}
public void Clear()
{
hashMap.Clear();
list.Clear();
keyList.Clear();
}
/// <summary>
/// index is an int as in NativeList not the key as in NativeHashMap sense.
/// For key use TryGetValue/TrySetValue.
/// </summary>
public TValue this[int index]
{
get => list[index];
set => list[index] = value;
}
public int Length => list.Length;
public bool TryGetValue(TKey key, out TValue value)
{
if (hashMap.TryGetValue(key, out int outIndex))
{
#if DEBUG_HML
UnityEngine.Debug.Log($"Key {key} at {outIndex} !");
#endif
value = list[outIndex];
#if DEBUG_HML
UnityEngine.Debug.Log($"Value is {value} !");
#endif
return true;
}
value = default;
return false;
}
public bool TrySetValue(TKey key, TValue value)
{
#if DEBUG_HML
UnityEngine.Debug.Log($"Try set {key} to {value}");
#endif
if (hashMap.TryGetValue(key, out int outIndex))
{
#if DEBUG_HML
UnityEngine.Debug.Log($"Setting at index {outIndex}");
#endif
list[outIndex] = value;
return true;
}
return false;
}
public bool TryAdd(TKey key, TValue value)
{
if (hashMap.TryAdd(key, list.Length))
{
list.Add(value);
keyList.Add(key);
#if DEBUG_HML
UnityEngine.Debug.Log($"Added {value} and key {key}");
#endif
return true;
}
return false;
}
/// <summary>
/// Change one key to other while keeping the same destination data.
/// </summary>
public bool TryChangeKey(TKey oldKey, TKey newKey)
{
if (hashMap.TryGetValue(oldKey, out int remember))
{
#if DEBUG_HML
UnityEngine.Debug.Log($"Changing key {oldKey} {remember}");
#endif
hashMap.Remove(oldKey);
if (hashMap.TryAdd(newKey, remember))
{
#if DEBUG_HML
UnityEngine.Debug.Log($"Changing to key {newKey} {remember}");
#endif
//Now update the corresponding keyList entry as well
keyList[remember] = newKey;
return true;
}
}
return false;
}
public void RemoveByKey(TKey key)
{
int backIndex = list.Length - 1;
if (!hashMap.TryGetValue(key, out int indexToRemove))
{
throw new System.Exception($"{key} does not exist in NativeHashMapList!");
}
bool swapUpdateNecessary = indexToRemove != backIndex;
TKey backKey = swapUpdateNecessary ? keyList[backIndex] : default;
#if DEBUG_HML
UnityEngine.Debug.Log($"Removing by key {key} index {indexToRemove}");
#endif
list.RemoveAtSwapBack(indexToRemove);
keyList.RemoveAtSwapBack(indexToRemove);
hashMap.Remove(key);
if (swapUpdateNecessary)
{
#if DEBUG_HML
UnityEngine.Debug.Log($"Swapping back key {backKey} from {backIndex} to {indexToRemove}");
#endif
//Need to update the dict entry that replace the swapped item.
hashMap.Remove(backKey);
//If remove success, TryAdd must success.
if (!hashMap.TryAdd(backKey, indexToRemove))
{
throw new NativeTouchTrackerException($"Why RemoveByKey can fail here???");
}
}
}
public void RemoveAtSwapBack(int index)
{
TKey backKey = keyList[list.Length - 1];
TKey removingKey = keyList[index];
list.RemoveAtSwapBack(index);
keyList.RemoveAtSwapBack(index);
hashMap.Remove(removingKey);
//Need to update the dict entry that results in the just-swapped index.
hashMap.Remove(backKey);
//If remove success, TryAdd must success.
if (!hashMap.TryAdd(backKey, index))
{
throw new NativeTouchTrackerException($"Why RemoveAtSwapBack can fail here???");
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment