From e2c683567e275aed8b346599beb4da5495713590 Mon Sep 17 00:00:00 2001 From: PopSlime Date: Fri, 24 Mar 2023 17:06:47 +0800 Subject: [PATCH] Introduce `IntKeyedDictionary` to improve performance. --- .../Common/Collections/HashHelpers.cs | 106 ++ .../Common/Collections/HashHelpers.cs.meta | 11 + .../Common/Collections/Specialized.meta | 8 + .../Specialized/IntKeyedDictionary.cs | 1037 +++++++++++++++++ .../Specialized/IntKeyedDictionary.cs.meta | 11 + Assets/Cryville/Crtr/Anchor.cs | 6 +- Assets/Cryville/Crtr/Chart.cs | 9 +- .../Cryville/Crtr/Components/SkinComponent.cs | 6 +- Assets/Cryville/Crtr/EffectManager.cs | 6 +- .../Cryville/Crtr/Event/ContainerHandler.cs | 9 +- Assets/Cryville/Crtr/Event/ContainerState.cs | 61 +- Assets/Cryville/Crtr/Event/MotionCache.cs | 11 +- Assets/Cryville/Crtr/Event/RMVPool.cs | 11 +- Assets/Cryville/Crtr/Judge.cs | 19 +- Assets/Cryville/Crtr/PdtEvaluator.cs | 16 +- Assets/Cryville/Crtr/PropOp.cs | 5 +- Assets/Cryville/Crtr/SkinContainer.cs | 7 +- Assets/Cryville/Crtr/SkinPropertyKey.cs | 29 +- 18 files changed, 1274 insertions(+), 94 deletions(-) create mode 100644 Assets/Cryville/Common/Collections/HashHelpers.cs create mode 100644 Assets/Cryville/Common/Collections/HashHelpers.cs.meta create mode 100644 Assets/Cryville/Common/Collections/Specialized.meta create mode 100644 Assets/Cryville/Common/Collections/Specialized/IntKeyedDictionary.cs create mode 100644 Assets/Cryville/Common/Collections/Specialized/IntKeyedDictionary.cs.meta diff --git a/Assets/Cryville/Common/Collections/HashHelpers.cs b/Assets/Cryville/Common/Collections/HashHelpers.cs new file mode 100644 index 0000000..6f52a9d --- /dev/null +++ b/Assets/Cryville/Common/Collections/HashHelpers.cs @@ -0,0 +1,106 @@ +using System; +using System.Diagnostics.Contracts; +using System.Runtime.CompilerServices; +using System.Runtime.ConstrainedExecution; +using System.Runtime.Serialization; +using System.Threading; + +namespace Cryville.Common.Collections { + internal static class HashHelpers { +#if FEATURE_RANDOMIZED_STRING_HASHING + public const int HashCollisionThreshold = 100; + public static bool s_UseRandomizedStringHashing = String.UseRandomizedHashing(); +#endif + + // Table of prime numbers to use as hash table sizes. + // A typical resize algorithm would pick the smallest prime number in this array + // that is larger than twice the previous capacity. + // Suppose our Hashtable currently has capacity x and enough elements are added + // such that a resize needs to occur. Resizing first computes 2x then finds the + // first prime in the table greater than 2x, i.e. if primes are ordered + // p_1, p_2, ..., p_i, ..., it finds p_n such that p_n-1 < 2x < p_n. + // Doubling is important for preserving the asymptotic complexity of the + // hashtable operations such as add. Having a prime guarantees that double + // hashing does not lead to infinite loops. IE, your hash function will be + // h1(key) + i*h2(key), 0 <= i < size. h2 and the size must be relatively prime. + public static readonly int[] primes = { + 3, 7, 11, 17, 23, 29, 37, 47, 59, 71, 89, 107, 131, 163, 197, 239, 293, 353, 431, 521, 631, 761, 919, + 1103, 1327, 1597, 1931, 2333, 2801, 3371, 4049, 4861, 5839, 7013, 8419, 10103, 12143, 14591, + 17519, 21023, 25229, 30293, 36353, 43627, 52361, 62851, 75431, 90523, 108631, 130363, 156437, + 187751, 225307, 270371, 324449, 389357, 467237, 560689, 672827, 807403, 968897, 1162687, 1395263, + 1674319, 2009191, 2411033, 2893249, 3471899, 4166287, 4999559, 5999471, 7199369}; + + // Used by Hashtable and Dictionary's SeralizationInfo .ctor's to store the SeralizationInfo + // object until OnDeserialization is called. + private static ConditionalWeakTable s_SerializationInfoTable; + + internal static ConditionalWeakTable SerializationInfoTable { + get { + if (s_SerializationInfoTable == null) { + ConditionalWeakTable newTable = new ConditionalWeakTable(); + Interlocked.CompareExchange(ref s_SerializationInfoTable, newTable, null); + } + + return s_SerializationInfoTable; + } + + } + + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + public static bool IsPrime(int candidate) { + if ((candidate & 1) != 0) { + int limit = (int)System.Math.Sqrt (candidate); + for (int divisor = 3; divisor <= limit; divisor += 2) { + if ((candidate % divisor) == 0) + return false; + } + return true; + } + return (candidate == 2); + } + + internal const Int32 HashPrime = 101; + + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + public static int GetPrime(int min) { + if (min < 0) + throw new ArgumentException("Hashtable's capacity overflowed and went negative. Check load factor, capacity and the current size of the table."); + Contract.EndContractBlock(); + + for (int i = 0; i < primes.Length; i++) { + int prime = primes[i]; + if (prime >= min) return prime; + } + + //outside of our predefined table. + //compute the hard way. + for (int i = (min | 1); i < Int32.MaxValue; i += 2) { + if (IsPrime(i) && ((i - 1) % HashPrime != 0)) + return i; + } + return min; + } + + public static int GetMinPrime() { + return primes[0]; + } + + // Returns size of hashtable to grow to. + public static int ExpandPrime(int oldSize) { + int newSize = 2 * oldSize; + + // Allow the hashtables to grow to maximum possible size (~2G elements) before encoutering capacity overflow. + // Note that this check works even when _items.Length overflowed thanks to the (uint) cast + if ((uint)newSize > MaxPrimeArrayLength && MaxPrimeArrayLength > oldSize) { + Contract.Assert(MaxPrimeArrayLength == GetPrime(MaxPrimeArrayLength), "Invalid MaxPrimeArrayLength"); + return MaxPrimeArrayLength; + } + + return GetPrime(newSize); + } + + + // This is the maximum prime smaller than Array.MaxArrayLength + public const int MaxPrimeArrayLength = 0x7FEFFFFD; + } +} diff --git a/Assets/Cryville/Common/Collections/HashHelpers.cs.meta b/Assets/Cryville/Common/Collections/HashHelpers.cs.meta new file mode 100644 index 0000000..d4d2b9c --- /dev/null +++ b/Assets/Cryville/Common/Collections/HashHelpers.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e7fe2c6f3299681448c1a546cce4dc65 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Cryville/Common/Collections/Specialized.meta b/Assets/Cryville/Common/Collections/Specialized.meta new file mode 100644 index 0000000..ac04281 --- /dev/null +++ b/Assets/Cryville/Common/Collections/Specialized.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f68b44d5226a73441b94e7dd5873529f +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Cryville/Common/Collections/Specialized/IntKeyedDictionary.cs b/Assets/Cryville/Common/Collections/Specialized/IntKeyedDictionary.cs new file mode 100644 index 0000000..5ddf1c9 --- /dev/null +++ b/Assets/Cryville/Common/Collections/Specialized/IntKeyedDictionary.cs @@ -0,0 +1,1037 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.Contracts; + +namespace Cryville.Common.Collections.Specialized { + /// + /// Represents a collection of keys and values. Identical to but much faster. + /// + /// The type of the values in the dictionary. + [DebuggerTypeProxy(typeof(IntKeyedDictionaryDebugView<>))] + [DebuggerDisplay("Count = {Count}")] + public class IntKeyedDictionary : IDictionary, IDictionary, IReadOnlyDictionary { + + private struct Entry { + public int next; // Index of next entry, -1 if last + public int key; // Key of entry + public T value; // Value of entry + } + + private int[] buckets; + private Entry[] entries; + private int count; + private int version; + private int freeList; + private int freeCount; + private KeyCollection keys; + private ValueCollection values; + private Object _syncRoot; + + public IntKeyedDictionary() : this(0) { } + + public IntKeyedDictionary(int capacity) { + if (capacity < 0) throw new ArgumentOutOfRangeException("capacity"); + if (capacity > 0) Initialize(capacity); + } + + public IntKeyedDictionary(IDictionary dictionary) : + this(dictionary != null ? dictionary.Count : 0) { + + if (dictionary == null) { + throw new ArgumentNullException("dictionary"); + } + + foreach (KeyValuePair pair in dictionary) { + Add(pair.Key, pair.Value); + } + } + + public int Count { + get { return count - freeCount; } + } + + public KeyCollection Keys { + get { + Contract.Ensures(Contract.Result() != null); + if (keys == null) keys = new KeyCollection(this); + return keys; + } + } + + ICollection IDictionary.Keys { + get { + if (keys == null) keys = new KeyCollection(this); + return keys; + } + } + + IEnumerable IReadOnlyDictionary.Keys { + get { + if (keys == null) keys = new KeyCollection(this); + return keys; + } + } + + public ValueCollection Values { + get { + Contract.Ensures(Contract.Result() != null); + if (values == null) values = new ValueCollection(this); + return values; + } + } + + ICollection IDictionary.Values { + get { + if (values == null) values = new ValueCollection(this); + return values; + } + } + + IEnumerable IReadOnlyDictionary.Values { + get { + if (values == null) values = new ValueCollection(this); + return values; + } + } + + public T this[int key] { + get { + int i = FindEntry(key); + if (i >= 0) return entries[i].value; + throw new KeyNotFoundException(); + } + set { + Insert(key, value, false); + } + } + + public void Add(int key, T value) { + Insert(key, value, true); + } + + void ICollection>.Add(KeyValuePair keyValuePair) { + Add(keyValuePair.Key, keyValuePair.Value); + } + + bool ICollection>.Contains(KeyValuePair keyValuePair) { + int i = FindEntry(keyValuePair.Key); + if (i >= 0) { + return true; + } + return false; + } + + bool ICollection>.Remove(KeyValuePair keyValuePair) { + int i = FindEntry(keyValuePair.Key); + if (i >= 0) { + Remove(keyValuePair.Key); + return true; + } + return false; + } + + public void Clear() { + if (count > 0) { + for (int i = 0; i < buckets.Length; i++) buckets[i] = -1; + Array.Clear(entries, 0, count); + freeList = -1; + count = 0; + freeCount = 0; + version++; + } + } + + public bool ContainsKey(int key) { + return FindEntry(key) >= 0; + } + + public bool ContainsValue(T value) { + if (value == null) { + for (int i = 0; i < count; i++) { + if (entries[i].key >= 0 && entries[i].value == null) return true; + } + } + else { + EqualityComparer c = EqualityComparer.Default; + for (int i = 0; i < count; i++) { + if (entries[i].key >= 0 && c.Equals(entries[i].value, value)) return true; + } + } + return false; + } + + private void CopyTo(KeyValuePair[] array, int index) { + if (array == null) { + throw new ArgumentNullException("array"); + } + + if (index < 0 || index > array.Length) { + throw new ArgumentOutOfRangeException("index", "Non-negative number required."); + } + + if (array.Length - index < Count) { + throw new ArgumentException("Destination array is not long enough to copy all the items in the collection. Check array index and length."); + } + + int count = this.count; + Entry[] entries = this.entries; + for (int i = 0; i < count; i++) { + if (entries[i].key >= 0) { + array[index++] = new KeyValuePair(entries[i].key, entries[i].value); + } + } + } + + public Enumerator GetEnumerator() { + return new Enumerator(this, Enumerator.KeyValuePair); + } + + IEnumerator> IEnumerable>.GetEnumerator() { + return new Enumerator(this, Enumerator.KeyValuePair); + } + + private int FindEntry(int key) { + if (buckets != null) { + for (int i = buckets[key % buckets.Length]; i >= 0; i = entries[i].next) { + if (entries[i].key == key) return i; + } + } + return -1; + } + + private void Initialize(int capacity) { + int size = HashHelpers.GetPrime(capacity); + buckets = new int[size]; + for (int i = 0; i < buckets.Length; i++) buckets[i] = -1; + entries = new Entry[size]; + freeList = -1; + } + + private void Insert(int key, T value, bool add) { + if (buckets == null) Initialize(0); + int targetBucket = key % buckets.Length; + +#if FEATURE_RANDOMIZED_STRING_HASHING + int collisionCount = 0; +#endif + + for (int i = buckets[targetBucket]; i >= 0; i = entries[i].next) { + if (entries[i].key == key) { + if (add) { + throw new ArgumentException("An item with the same key has already been added."); + } + entries[i].value = value; + version++; + return; + } + +#if FEATURE_RANDOMIZED_STRING_HASHING + collisionCount++; +#endif + } + int index; + if (freeCount > 0) { + index = freeList; + freeList = entries[index].next; + freeCount--; + } + else { + if (count == entries.Length) { + Resize(); + targetBucket = key % buckets.Length; + } + index = count; + count++; + } + + entries[index].next = buckets[targetBucket]; + entries[index].key = key; + entries[index].value = value; + buckets[targetBucket] = index; + version++; + } + + private void Resize() { + Resize(HashHelpers.ExpandPrime(count), false); + } + + private void Resize(int newSize, bool forceNewHashCodes) { + Contract.Assert(newSize >= entries.Length); + int[] newBuckets = new int[newSize]; + for (int i = 0; i < newBuckets.Length; i++) newBuckets[i] = -1; + Entry[] newEntries = new Entry[newSize]; + Array.Copy(entries, 0, newEntries, 0, count); + if (forceNewHashCodes) { + for (int i = 0; i < count; i++) { + if (newEntries[i].key != -1) { + newEntries[i].key = newEntries[i].key; + } + } + } + for (int i = 0; i < count; i++) { + if (newEntries[i].key >= 0) { + int bucket = newEntries[i].key % newSize; + newEntries[i].next = newBuckets[bucket]; + newBuckets[bucket] = i; + } + } + buckets = newBuckets; + entries = newEntries; + } + + public bool Remove(int key) { + if (buckets != null) { + int bucket = key % buckets.Length; + int last = -1; + for (int i = buckets[bucket]; i >= 0; last = i, i = entries[i].next) { + if (entries[i].key == key) { + if (last < 0) { + buckets[bucket] = entries[i].next; + } + else { + entries[last].next = entries[i].next; + } + entries[i].key = -1; + entries[i].next = freeList; + entries[i].value = default(T); + freeList = i; + freeCount++; + version++; + return true; + } + } + } + return false; + } + + public bool TryGetValue(int key, out T value) { + int i = FindEntry(key); + if (i >= 0) { + value = entries[i].value; + return true; + } + value = default(T); + return false; + } + + // This is a convenience method for the internal callers that were converted from using Hashtable. + // Many were combining key doesn't exist and key exists but null value (for non-value types) checks. + // This allows them to continue getting that behavior with minimal code delta. This is basically + // TryGetValue without the out param + internal T GetValueOrDefault(int key) { + int i = FindEntry(key); + if (i >= 0) { + return entries[i].value; + } + return default(T); + } + + bool ICollection>.IsReadOnly { + get { return false; } + } + + void ICollection>.CopyTo(KeyValuePair[] array, int index) { + CopyTo(array, index); + } + + void ICollection.CopyTo(Array array, int index) { + if (array == null) { + throw new ArgumentNullException("array"); + } + + if (array.Rank != 1) { + throw new ArgumentException("Only single dimensional arrays are supported for the requested action."); + } + + if (array.GetLowerBound(0) != 0) { + throw new ArgumentException("The lower bound of target array must be zero."); + } + + if (index < 0 || index > array.Length) { + throw new ArgumentOutOfRangeException("index", "Non-negative number required."); + } + + if (array.Length - index < Count) { + throw new ArgumentException("Destination array is not long enough to copy all the items in the collection. Check array index and length."); + } + + KeyValuePair[] pairs = array as KeyValuePair[]; + if (pairs != null) { + CopyTo(pairs, index); + } + else if (array is DictionaryEntry[]) { + DictionaryEntry[] dictEntryArray = array as DictionaryEntry[]; + Entry[] entries = this.entries; + for (int i = 0; i < count; i++) { + if (entries[i].key >= 0) { + dictEntryArray[index++] = new DictionaryEntry(entries[i].key, entries[i].value); + } + } + } + else { + object[] objects = array as object[]; + if (objects == null) { + throw new ArgumentException("Target array type is not compatible with the type of items in the collection."); + } + + try { + int count = this.count; + Entry[] entries = this.entries; + for (int i = 0; i < count; i++) { + if (entries[i].key >= 0) { + objects[index++] = new KeyValuePair(entries[i].key, entries[i].value); + } + } + } + catch (ArrayTypeMismatchException) { + throw new ArgumentException("Target array type is not compatible with the type of items in the collection."); + } + } + } + + IEnumerator IEnumerable.GetEnumerator() { + return new Enumerator(this, Enumerator.KeyValuePair); + } + + bool ICollection.IsSynchronized { + get { return false; } + } + + object ICollection.SyncRoot { + get { + if (_syncRoot == null) { + System.Threading.Interlocked.CompareExchange(ref _syncRoot, new Object(), null); + } + return _syncRoot; + } + } + + bool IDictionary.IsFixedSize { + get { return false; } + } + + bool IDictionary.IsReadOnly { + get { return false; } + } + + ICollection IDictionary.Keys { + get { return (ICollection)Keys; } + } + + ICollection IDictionary.Values { + get { return (ICollection)Values; } + } + + object IDictionary.this[object key] { + get { + if (IsCompatibleKey(key)) { + int i = FindEntry((int)key); + if (i >= 0) { + return entries[i].value; + } + } + return null; + } + set { + if (key == null) { + throw new ArgumentNullException("key"); + } + if (value == null && default(T) != null) { + throw new ArgumentNullException("value"); + } + + try { + int tempKey = (int)key; + try { + this[tempKey] = (T)value; + } + catch (InvalidCastException) { + throw new ArgumentException(string.Format("The value \"{0}\" is not of type \"{1}\" and cannot be used in this generic collection.", value, typeof(T)), "value"); + } + } + catch (InvalidCastException) { + throw new ArgumentException(string.Format("The value \"{0}\" is not of type \"{1}\" and cannot be used in this generic collection.", key, typeof(int)), "key"); + } + } + } + + private static bool IsCompatibleKey(object key) { + if (key == null) { + throw new ArgumentNullException("key"); + } + return (key is int); + } + + void IDictionary.Add(object key, object value) { + if (key == null) { + throw new ArgumentNullException("key"); + } + if (value == null && default(T) != null) { + throw new ArgumentNullException("value"); + } + + try { + int tempKey = (int)key; + + try { + Add(tempKey, (T)value); + } + catch (InvalidCastException) { + throw new ArgumentException(string.Format("The value \"{0}\" is not of type \"{1}\" and cannot be used in this generic collection.", value, typeof(T)), "value"); + } + } + catch (InvalidCastException) { + throw new ArgumentException(string.Format("The value \"{0}\" is not of type \"{1}\" and cannot be used in this generic collection.", key, typeof(int)), "key"); + } + } + + bool IDictionary.Contains(object key) { + if (IsCompatibleKey(key)) { + return ContainsKey((int)key); + } + + return false; + } + + IDictionaryEnumerator IDictionary.GetEnumerator() { + return new Enumerator(this, Enumerator.DictEntry); + } + + void IDictionary.Remove(object key) { + if (IsCompatibleKey(key)) { + Remove((int)key); + } + } + + [Serializable] + public struct Enumerator : IEnumerator>, + IDictionaryEnumerator { + private readonly IntKeyedDictionary dictionary; + private readonly int version; + private int index; + private KeyValuePair current; + private readonly int getEnumeratorRetType; // What should Enumerator.Current return? + + internal const int DictEntry = 1; + internal const int KeyValuePair = 2; + + internal Enumerator(IntKeyedDictionary dictionary, int getEnumeratorRetType) { + this.dictionary = dictionary; + version = dictionary.version; + index = 0; + this.getEnumeratorRetType = getEnumeratorRetType; + current = new KeyValuePair(); + } + + public bool MoveNext() { + if (version != dictionary.version) { + throw new InvalidOperationException("Collection was modified; enumeration operation may not execute."); + } + + // Use unsigned comparison since we set index to dictionary.count+1 when the enumeration ends. + // dictionary.count+1 could be negative if dictionary.count is Int32.MaxValue + while ((uint)index < (uint)dictionary.count) { + if (dictionary.entries[index].key >= 0) { + current = new KeyValuePair(dictionary.entries[index].key, dictionary.entries[index].value); + index++; + return true; + } + index++; + } + + index = dictionary.count + 1; + current = new KeyValuePair(); + return false; + } + + public KeyValuePair Current { + get { return current; } + } + + public void Dispose() { + } + + object IEnumerator.Current { + get { + if (index == 0 || (index == dictionary.count + 1)) { + throw new InvalidOperationException("Enumeration has either not started or has already finished."); + } + + if (getEnumeratorRetType == DictEntry) { + return new System.Collections.DictionaryEntry(current.Key, current.Value); + } + else { + return new KeyValuePair(current.Key, current.Value); + } + } + } + + void IEnumerator.Reset() { + if (version != dictionary.version) { + throw new InvalidOperationException("Collection was modified; enumeration operation may not execute."); + } + + index = 0; + current = new KeyValuePair(); + } + + DictionaryEntry IDictionaryEnumerator.Entry { + get { + if (index == 0 || (index == dictionary.count + 1)) { + throw new InvalidOperationException("Enumeration has either not started or has already finished."); + } + + return new DictionaryEntry(current.Key, current.Value); + } + } + + object IDictionaryEnumerator.Key { + get { + if (index == 0 || (index == dictionary.count + 1)) { + throw new InvalidOperationException("Enumeration has either not started or has already finished."); + } + + return current.Key; + } + } + + object IDictionaryEnumerator.Value { + get { + if (index == 0 || (index == dictionary.count + 1)) { + throw new InvalidOperationException("Enumeration has either not started or has already finished."); + } + + return current.Value; + } + } + } + + [DebuggerTypeProxy(typeof(IntKeyedDictionaryKeyCollectionDebugView<>))] + [DebuggerDisplay("Count = {Count}")] + [Serializable] + public sealed class KeyCollection : ICollection, ICollection, IReadOnlyCollection { + private readonly IntKeyedDictionary dictionary; + + public KeyCollection(IntKeyedDictionary dictionary) { + if (dictionary == null) { + throw new ArgumentNullException("dictionary"); + } + this.dictionary = dictionary; + } + + public Enumerator GetEnumerator() { + return new Enumerator(dictionary); + } + + public void CopyTo(int[] array, int index) { + if (array == null) { + throw new ArgumentNullException("array"); + } + + if (index < 0 || index > array.Length) { + throw new ArgumentOutOfRangeException("index", "Non-negative number required."); + } + + if (array.Length - index < dictionary.Count) { + throw new ArgumentException("Destination array is not long enough to copy all the items in the collection. Check array index and length."); + } + + int count = dictionary.count; + Entry[] entries = dictionary.entries; + for (int i = 0; i < count; i++) { + if (entries[i].key >= 0) array[index++] = entries[i].key; + } + } + + public int Count { + get { return dictionary.Count; } + } + + bool ICollection.IsReadOnly { + get { return true; } + } + + void ICollection.Add(int item) { + throw new NotSupportedException("Mutating a key collection derived from a dictionary is not allowed."); + } + + void ICollection.Clear() { + throw new NotSupportedException("Mutating a key collection derived from a dictionary is not allowed."); + } + + bool ICollection.Contains(int item) { + return dictionary.ContainsKey(item); + } + + bool ICollection.Remove(int item) { + throw new NotSupportedException("Mutating a key collection derived from a dictionary is not allowed."); + } + + IEnumerator IEnumerable.GetEnumerator() { + return new Enumerator(dictionary); + } + + IEnumerator IEnumerable.GetEnumerator() { + return new Enumerator(dictionary); + } + + void ICollection.CopyTo(Array array, int index) { + if (array == null) { + throw new ArgumentNullException("array"); + } + + if (array.Rank != 1) { + throw new ArgumentException("Only single dimensional arrays are supported for the requested action."); + } + + if (array.GetLowerBound(0) != 0) { + throw new ArgumentException("The lower bound of target array must be zero."); + } + + if (index < 0 || index > array.Length) { + throw new ArgumentOutOfRangeException("index", "Non-negative number required."); + } + + if (array.Length - index < dictionary.Count) { + throw new ArgumentException("Destination array is not long enough to copy all the items in the collection. Check array index and length."); + } + + int[] keys = array as int[]; + if (keys != null) { + CopyTo(keys, index); + } + else { + object[] objects = array as object[]; + if (objects == null) { + throw new ArgumentException("Target array type is not compatible with the type of items in the collection."); + } + + int count = dictionary.count; + Entry[] entries = dictionary.entries; + try { + for (int i = 0; i < count; i++) { + if (entries[i].key >= 0) objects[index++] = entries[i].key; + } + } + catch (ArrayTypeMismatchException) { + throw new ArgumentException("Target array type is not compatible with the type of items in the collection."); + } + } + } + + bool ICollection.IsSynchronized { + get { return false; } + } + + Object ICollection.SyncRoot { + get { return ((ICollection)dictionary).SyncRoot; } + } + + [Serializable] + public struct Enumerator : IEnumerator, System.Collections.IEnumerator { + private readonly IntKeyedDictionary dictionary; + private int index; + private readonly int version; + private int currentKey; + + internal Enumerator(IntKeyedDictionary dictionary) { + this.dictionary = dictionary; + version = dictionary.version; + index = 0; + currentKey = default(int); + } + + public void Dispose() { + } + + public bool MoveNext() { + if (version != dictionary.version) { + throw new InvalidOperationException("Collection was modified; enumeration operation may not execute."); + } + + while ((uint)index < (uint)dictionary.count) { + if (dictionary.entries[index].key >= 0) { + currentKey = dictionary.entries[index].key; + index++; + return true; + } + index++; + } + + index = dictionary.count + 1; + currentKey = default(int); + return false; + } + + public int Current { + get { + return currentKey; + } + } + + Object System.Collections.IEnumerator.Current { + get { + if (index == 0 || (index == dictionary.count + 1)) { + throw new InvalidOperationException("Enumeration has either not started or has already finished."); + } + + return currentKey; + } + } + + void System.Collections.IEnumerator.Reset() { + if (version != dictionary.version) { + throw new InvalidOperationException("Collection was modified; enumeration operation may not execute."); + } + + index = 0; + currentKey = default(int); + } + } + } + + [DebuggerTypeProxy(typeof(IntKeyedDictionaryValueCollectionDebugView<>))] + [DebuggerDisplay("Count = {Count}")] + [Serializable] + public sealed class ValueCollection : ICollection, ICollection, IReadOnlyCollection { + private readonly IntKeyedDictionary dictionary; + + public ValueCollection(IntKeyedDictionary dictionary) { + if (dictionary == null) { + throw new ArgumentNullException("dictionary"); + } + this.dictionary = dictionary; + } + + public Enumerator GetEnumerator() { + return new Enumerator(dictionary); + } + + public void CopyTo(T[] array, int index) { + if (array == null) { + throw new ArgumentNullException("array"); + } + + if (index < 0 || index > array.Length) { + throw new ArgumentOutOfRangeException("index", "Non-negative number required."); + } + + if (array.Length - index < dictionary.Count) { + throw new ArgumentException("Destination array is not long enough to copy all the items in the collection. Check array index and length."); + } + + int count = dictionary.count; + Entry[] entries = dictionary.entries; + for (int i = 0; i < count; i++) { + if (entries[i].key >= 0) array[index++] = entries[i].value; + } + } + + public int Count { + get { return dictionary.Count; } + } + + bool ICollection.IsReadOnly { + get { return true; } + } + + void ICollection.Add(T item) { + throw new NotSupportedException("Mutating a value collection derived from a dictionary is not allowed."); + } + + bool ICollection.Remove(T item) { + throw new NotSupportedException("Mutating a value collection derived from a dictionary is not allowed."); + } + + void ICollection.Clear() { + throw new NotSupportedException("Mutating a value collection derived from a dictionary is not allowed."); + } + + bool ICollection.Contains(T item) { + return dictionary.ContainsValue(item); + } + + IEnumerator IEnumerable.GetEnumerator() { + return new Enumerator(dictionary); + } + + IEnumerator IEnumerable.GetEnumerator() { + return new Enumerator(dictionary); + } + + void ICollection.CopyTo(Array array, int index) { + if (array == null) { + throw new ArgumentNullException("array"); + } + + if (array.Rank != 1) { + throw new ArgumentException("Only single dimensional arrays are supported for the requested action."); + } + + if (array.GetLowerBound(0) != 0) { + throw new ArgumentException("The lower bound of target array must be zero."); + } + + if (index < 0 || index > array.Length) { + throw new ArgumentOutOfRangeException("index", "Non-negative number required."); + } + + if (array.Length - index < dictionary.Count) { + throw new ArgumentException("Destination array is not long enough to copy all the items in the collection. Check array index and length."); + } + + T[] values = array as T[]; + if (values != null) { + CopyTo(values, index); + } + else { + object[] objects = array as object[]; + if (objects == null) { + throw new ArgumentException("Target array type is not compatible with the type of items in the collection."); + } + + int count = dictionary.count; + Entry[] entries = dictionary.entries; + try { + for (int i = 0; i < count; i++) { + if (entries[i].key >= 0) objects[index++] = entries[i].value; + } + } + catch (ArrayTypeMismatchException) { + throw new ArgumentException("Target array type is not compatible with the type of items in the collection."); + } + } + } + + bool ICollection.IsSynchronized { + get { return false; } + } + + Object ICollection.SyncRoot { + get { return ((ICollection)dictionary).SyncRoot; } + } + + [Serializable] + public struct Enumerator : IEnumerator, IEnumerator { + private readonly IntKeyedDictionary dictionary; + private int index; + private readonly int version; + private T currentValue; + + internal Enumerator(IntKeyedDictionary dictionary) { + this.dictionary = dictionary; + version = dictionary.version; + index = 0; + currentValue = default(T); + } + + public void Dispose() { + } + + public bool MoveNext() { + if (version != dictionary.version) { + throw new InvalidOperationException("Collection was modified; enumeration operation may not execute."); + } + + while ((uint)index < (uint)dictionary.count) { + if (dictionary.entries[index].key >= 0) { + currentValue = dictionary.entries[index].value; + index++; + return true; + } + index++; + } + index = dictionary.count + 1; + currentValue = default(T); + return false; + } + + public T Current { + get { + return currentValue; + } + } + + Object System.Collections.IEnumerator.Current { + get { + if (index == 0 || (index == dictionary.count + 1)) { + throw new InvalidOperationException("Enumeration has either not started or has already finished."); + } + + return currentValue; + } + } + + void System.Collections.IEnumerator.Reset() { + if (version != dictionary.version) { + throw new InvalidOperationException("Collection was modified; enumeration operation may not execute."); + } + index = 0; + currentValue = default(T); + } + } + } + } + + internal sealed class IntKeyedDictionaryDebugView { + private readonly IntKeyedDictionary dict; + + public IntKeyedDictionaryDebugView(IntKeyedDictionary dictionary) { + if (dictionary == null) + throw new ArgumentNullException("dictionary"); + + this.dict = dictionary; + } + + [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] + public KeyValuePair[] Items { + get { + KeyValuePair[] items = new KeyValuePair[dict.Count]; + ((IDictionary)dict).CopyTo(items, 0); + return items; + } + } + } + + internal sealed class IntKeyedDictionaryKeyCollectionDebugView { + private readonly IntKeyedDictionary collection; + + public IntKeyedDictionaryKeyCollectionDebugView(IntKeyedDictionary collection) { + if (collection == null) + throw new ArgumentNullException("collection"); + + this.collection = collection; + } + + [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] + public T[] Items { + get { + T[] items = new T[collection.Count]; + ((IDictionary)collection).CopyTo(items, 0); + return items; + } + } + } + + internal sealed class IntKeyedDictionaryValueCollectionDebugView { + private readonly IntKeyedDictionary collection; + + public IntKeyedDictionaryValueCollectionDebugView(IntKeyedDictionary collection) { + if (collection == null) + throw new ArgumentNullException("collection"); + + this.collection = collection; + } + + [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] + public T[] Items { + get { + T[] items = new T[collection.Count]; + ((IDictionary)collection).CopyTo(items, 0); + return items; + } + } + } +} diff --git a/Assets/Cryville/Common/Collections/Specialized/IntKeyedDictionary.cs.meta b/Assets/Cryville/Common/Collections/Specialized/IntKeyedDictionary.cs.meta new file mode 100644 index 0000000..15217eb --- /dev/null +++ b/Assets/Cryville/Common/Collections/Specialized/IntKeyedDictionary.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 634536d804abc784394d4ac028a77879 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Cryville/Crtr/Anchor.cs b/Assets/Cryville/Crtr/Anchor.cs index f9ddf2d..83a97e8 100644 --- a/Assets/Cryville/Crtr/Anchor.cs +++ b/Assets/Cryville/Crtr/Anchor.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using Cryville.Common.Collections.Specialized; using UnityEngine; namespace Cryville.Crtr { @@ -6,11 +6,11 @@ namespace Cryville.Crtr { public int Name { get; private set; } public Transform Transform { get; private set; } public SkinContext SkinContext { get; private set; } - public Dictionary PropSrcs { get; private set; } + public IntKeyedDictionary PropSrcs { get; private set; } public Anchor(int name, Transform transform, int propSrcCount = 0) { Name = name; Transform = transform; - if (propSrcCount > 0) PropSrcs = new Dictionary(propSrcCount); + if (propSrcCount > 0) PropSrcs = new IntKeyedDictionary(propSrcCount); SkinContext = new SkinContext(transform, PropSrcs); } } diff --git a/Assets/Cryville/Crtr/Chart.cs b/Assets/Cryville/Crtr/Chart.cs index 75ef9ec..9eef600 100644 --- a/Assets/Cryville/Crtr/Chart.cs +++ b/Assets/Cryville/Crtr/Chart.cs @@ -1,4 +1,5 @@ using Cryville.Common; +using Cryville.Common.Collections.Specialized; using Newtonsoft.Json; using System; using System.Collections.Generic; @@ -142,19 +143,19 @@ namespace Cryville.Crtr { } [JsonIgnore] - public Dictionary PropSrcs { get; private set; } + public IntKeyedDictionary PropSrcs { get; private set; } protected void SubmitPropSrc(string name, PropSrc property) { PropSrcs.Add(IdentifierManager.SharedInstance.Request(name), property); } [JsonIgnore] - public Dictionary PropOps { get; private set; } + public IntKeyedDictionary PropOps { get; private set; } protected void SubmitPropOp(string name, PropOp property) { PropOps.Add(IdentifierManager.SharedInstance.Request(name), property); } protected ChartEvent() { - PropSrcs = new Dictionary(); - PropOps = new Dictionary(); + PropSrcs = new IntKeyedDictionary(); + PropOps = new IntKeyedDictionary(); SubmitPropSrc("event", new PropSrc.Float(() => { int hash = GetHashCode(); return Unsafe.As(ref hash); diff --git a/Assets/Cryville/Crtr/Components/SkinComponent.cs b/Assets/Cryville/Crtr/Components/SkinComponent.cs index 6787ceb..0c8de82 100644 --- a/Assets/Cryville/Crtr/Components/SkinComponent.cs +++ b/Assets/Cryville/Crtr/Components/SkinComponent.cs @@ -1,6 +1,6 @@ using Cryville.Common; +using Cryville.Common.Collections.Specialized; using Cryville.Common.Pdt; -using System.Collections.Generic; using UnityEngine; namespace Cryville.Crtr.Components { @@ -8,7 +8,7 @@ namespace Cryville.Crtr.Components { /// /// The property operators of the component. /// - public Dictionary Properties { get; private set; } + public IntKeyedDictionary Properties { get; private set; } /// /// Submits a property. /// @@ -22,7 +22,7 @@ namespace Cryville.Crtr.Components { /// Creates a skin component. /// protected SkinComponent() { - Properties = new Dictionary(); + Properties = new IntKeyedDictionary(); } public virtual void Init() { } diff --git a/Assets/Cryville/Crtr/EffectManager.cs b/Assets/Cryville/Crtr/EffectManager.cs index 11432c8..4a52ce8 100644 --- a/Assets/Cryville/Crtr/EffectManager.cs +++ b/Assets/Cryville/Crtr/EffectManager.cs @@ -1,10 +1,10 @@ -using System.Collections.Generic; +using Cryville.Common.Collections.Specialized; using UnityEngine; namespace Cryville.Crtr { public class EffectManager { - readonly Dictionary _groups - = new Dictionary(); + readonly IntKeyedDictionary _groups + = new IntKeyedDictionary(); public EffectManager(PdtSkin skin) { foreach (var e in skin.effects) { _groups.Add(e.Key.Key, new EffectGroup(e.Value)); diff --git a/Assets/Cryville/Crtr/Event/ContainerHandler.cs b/Assets/Cryville/Crtr/Event/ContainerHandler.cs index c103cc3..87875ef 100644 --- a/Assets/Cryville/Crtr/Event/ContainerHandler.cs +++ b/Assets/Cryville/Crtr/Event/ContainerHandler.cs @@ -1,5 +1,6 @@ using Cryville.Common; using Cryville.Common.Buffers; +using Cryville.Common.Collections.Specialized; using Cryville.Crtr.Components; using System; using System.Collections.Generic; @@ -65,7 +66,7 @@ namespace Cryville.Crtr.Event { static readonly int _var_current_time = IdentifierManager.SharedInstance.Request("current_time"); static readonly int _var_invisible_bounds = IdentifierManager.SharedInstance.Request("invisible_bounds"); - public readonly Dictionary PropSrcs = new Dictionary(); + public readonly IntKeyedDictionary PropSrcs = new IntKeyedDictionary(); SkinContainer skinContainer; protected Judge judge; public void AttachSystems(PdtSkin skin, Judge judge) { @@ -73,9 +74,9 @@ namespace Cryville.Crtr.Event { this.judge = judge; } - public readonly Dictionary> Anchors = new Dictionary>(); - public readonly Dictionary DynamicAnchors = new Dictionary(); - public readonly Dictionary DynamicAnchorSetTime = new Dictionary(); + public readonly IntKeyedDictionary> Anchors = new IntKeyedDictionary>(); + public readonly IntKeyedDictionary DynamicAnchors = new IntKeyedDictionary(); + public readonly IntKeyedDictionary DynamicAnchorSetTime = new IntKeyedDictionary(); Anchor a_cur; Anchor a_head; Anchor a_tail; diff --git a/Assets/Cryville/Crtr/Event/ContainerState.cs b/Assets/Cryville/Crtr/Event/ContainerState.cs index 4a9b027..4a5abf9 100644 --- a/Assets/Cryville/Crtr/Event/ContainerState.cs +++ b/Assets/Cryville/Crtr/Event/ContainerState.cs @@ -2,6 +2,7 @@ using Cryville.Common; using Cryville.Common.Buffers; +using Cryville.Common.Collections.Specialized; using System; using System.Collections.Generic; using System.Runtime.CompilerServices; @@ -91,13 +92,13 @@ namespace Cryville.Crtr.Event { Parent = parent; } - _rmvpa = new CategorizedPoolAccessor(RMVPool); - _mcpa = new CategorizedPoolAccessor(MCPool); + _rmvpa = new CategorizedPoolAccessor(RMVPool); + _mcpa = new CategorizedPoolAccessor(MCPool); - Values = new Dictionary(ChartPlayer.motionRegistry.Count); - CachedValues = new Dictionary(ChartPlayer.motionRegistry.Count); + Values = new IntKeyedDictionary(ChartPlayer.motionRegistry.Count); + CachedValues = new IntKeyedDictionary(ChartPlayer.motionRegistry.Count); foreach (var m in ChartPlayer.motionRegistry) - Values.Add(m.Key, new RealtimeMotionValue().Init(Parent == null ? m.Value.GlobalInitValue : m.Value.InitValue)); + Values.Add(m.Key.Key, new RealtimeMotionValue().Init(Parent == null ? m.Value.GlobalInitValue : m.Value.InitValue)); rootPrototype = this; } @@ -114,13 +115,13 @@ namespace Cryville.Crtr.Event { public ContainerState Clone(byte ct) { var r = (ContainerState)MemberwiseClone(); - var mvs = new Dictionary(ChartPlayer.motionRegistry.Count); + var mvs = new IntKeyedDictionary(ChartPlayer.motionRegistry.Count); foreach (var mv in Values) { mvs.Add(mv.Key, mv.Value.Clone()); } r.Values = mvs; - var cvs = new Dictionary(ChartPlayer.motionRegistry.Count); + var cvs = new IntKeyedDictionary(ChartPlayer.motionRegistry.Count); r.CachedValues = cvs; r.Children = new Dictionary(); @@ -218,11 +219,11 @@ namespace Cryville.Crtr.Event { #region Motion internal static RMVPool RMVPool; internal static MotionCachePool MCPool; - readonly CategorizedPoolAccessor _rmvpa; - readonly CategorizedPoolAccessor _mcpa; + readonly CategorizedPoolAccessor _rmvpa; + readonly CategorizedPoolAccessor _mcpa; Dictionary PlayingMotions = new Dictionary(4); - Dictionary Values; - Dictionary CachedValues; + IntKeyedDictionary Values; + IntKeyedDictionary CachedValues; /// /// Gets a motion value. @@ -230,13 +231,13 @@ namespace Cryville.Crtr.Event { /// The motion name. /// Returns a cloned motion value instead. /// A motion value. - RealtimeMotionValue GetMotionValue(Identifier name, bool clone = false) { + RealtimeMotionValue GetMotionValue(int name, bool clone = false) { RealtimeMotionValue value = Values[name]; if (clone) return value.Clone(); return value; } - void InvalidateMotion(Identifier name) { + void InvalidateMotion(int name) { MotionCache cache; if (!CachedValues.TryGetValue(name, out cache)) CachedValues.Add(name, cache = _mcpa.Rent(name)); @@ -245,7 +246,7 @@ namespace Cryville.Crtr.Event { Children[c].InvalidateMotion(name); } - public Vector GetRawValue(Identifier key) { + public Vector GetRawValue(int key) { MotionCache tr; if (!CachedValues.TryGetValue(key, out tr)) CachedValues.Add(key, tr = _mcpa.Rent(key)); @@ -263,11 +264,11 @@ namespace Cryville.Crtr.Event { return r; } - public T GetRawValue(Identifier key) where T : Vector { + public T GetRawValue(int key) where T : Vector { return (T)GetRawValue(key); } - static readonly Identifier n_pt = new Identifier("pt"); + static readonly int n_pt = IdentifierManager.SharedInstance.Request("pt"); public Vector2 ScreenPoint { get { var mv = GetRawValue(n_pt); @@ -275,7 +276,7 @@ namespace Cryville.Crtr.Event { } } - static readonly Identifier n_dir = new Identifier("dir"); + static readonly int n_dir = IdentifierManager.SharedInstance.Request("dir"); public Vector3 Direction { get { Vec3 r = GetRawValue(n_dir); @@ -283,7 +284,7 @@ namespace Cryville.Crtr.Event { } } - static readonly Identifier n_normal = new Identifier("normal"); + static readonly int n_normal = IdentifierManager.SharedInstance.Request("normal"); public Vector3 Normal { get { Vec3 r = GetRawValue(n_normal); @@ -297,8 +298,8 @@ namespace Cryville.Crtr.Event { } } - static readonly Identifier n_sv = new Identifier("sv"); - static readonly Identifier n_svm = new Identifier("svm"); + static readonly int n_sv = IdentifierManager.SharedInstance.Request("sv"); + static readonly int n_svm = IdentifierManager.SharedInstance.Request("svm"); public float ScrollVelocity { get { return GetRawValue(n_sv).ToFloat(ChartPlayer.hitRect) @@ -306,7 +307,7 @@ namespace Cryville.Crtr.Event { } } - static readonly Identifier n_dist = new Identifier("dist"); + static readonly int n_dist = IdentifierManager.SharedInstance.Request("dist"); public float Distance { get { var mv = GetRawValue(n_dist); @@ -314,15 +315,15 @@ namespace Cryville.Crtr.Event { } } - static readonly Identifier n_corner = new Identifier("corner"); + static readonly int n_corner = IdentifierManager.SharedInstance.Request("corner"); public bool Corner { get { return GetRawValue(n_corner).Value % 2 >= 1; } } - static readonly Identifier n_ctrl0 = new Identifier("ctrl0"); - static readonly Identifier n_ctrl1 = new Identifier("ctrl1"); + static readonly int n_ctrl0 = IdentifierManager.SharedInstance.Request("ctrl0"); + static readonly int n_ctrl1 = IdentifierManager.SharedInstance.Request("ctrl1"); public Vector3 GetControlPoint(bool alt1, float deltaz) { var mv = GetRawValue(alt1 ? n_ctrl1 : n_ctrl0); if (alt1 && mv.IsZero()) { @@ -331,7 +332,7 @@ namespace Cryville.Crtr.Event { return mv.ToVector3(ChartPlayer.hitRect, deltaz); } - static readonly Identifier n_track = new Identifier("track"); + static readonly int n_track = IdentifierManager.SharedInstance.Request("track"); public float Track { get { return GetRawValue(n_track).Value; @@ -357,9 +358,9 @@ namespace Cryville.Crtr.Event { if (ev != null) { if (ev.Unstamped is Chart.Motion) { var tev = (Chart.Motion)ev.Unstamped; - var mv = _rmvpa.Rent(tev.Name); + var mv = _rmvpa.Rent(tev.Name.Key); mv.CloneTypeFlag = CloneType; - GetMotionValue(tev.Name).CopyTo(mv); + GetMotionValue(tev.Name.Key).CopyTo(mv); PlayingMotions.Add(ev, mv); Update(ev); if (!ev.Unstamped.IsLong) { @@ -407,8 +408,8 @@ namespace Cryville.Crtr.Event { foreach (var m in PlayingMotions) { var tev = (Chart.Motion)m.Key.Unstamped; if (tev.RelativeNode != null && CloneType == 2) continue; - var value = GetMotionValue(tev.Name/*, true*/); - InvalidateMotion(tev.Name); + var value = GetMotionValue(tev.Name.Key/*, true*/); + InvalidateMotion(tev.Name.Key); if (m.Key.Duration == 0) { if (tev.RelativeNode != null) { value.SetRelativeNode(tev.RelativeNode); @@ -428,7 +429,7 @@ namespace Cryville.Crtr.Event { tev.AbsoluteValue.LerpWith(m.Value.AbsoluteValue, lerpedTime, ref value.AbsoluteValue); } } - Values[tev.Name] = value; + Values[tev.Name.Key] = value; } } diff --git a/Assets/Cryville/Crtr/Event/MotionCache.cs b/Assets/Cryville/Crtr/Event/MotionCache.cs index 8f58b91..540024d 100644 --- a/Assets/Cryville/Crtr/Event/MotionCache.cs +++ b/Assets/Cryville/Crtr/Event/MotionCache.cs @@ -1,5 +1,6 @@ using Cryville.Common; using Cryville.Common.Buffers; +using Cryville.Common.Collections.Specialized; using System.Collections.Generic; namespace Cryville.Crtr.Event { @@ -11,7 +12,7 @@ namespace Cryville.Crtr.Event { Value.CopyTo(dest.Value); } } - internal class MotionCachePool : CategorizedPool { + internal class MotionCachePool : CategorizedPool { private class Bucket : ObjectPool { readonly MotionRegistry _reg; public Bucket(Identifier name, int capacity) : base(capacity) { @@ -26,12 +27,12 @@ namespace Cryville.Crtr.Event { obj.Valid = false; } } - readonly Dictionary> m_buckets; - protected override IReadOnlyDictionary> Buckets { get { return m_buckets; } } + readonly IntKeyedDictionary> m_buckets; + protected override IReadOnlyDictionary> Buckets { get { return m_buckets; } } public MotionCachePool() { - m_buckets = new Dictionary>(ChartPlayer.motionRegistry.Count); + m_buckets = new IntKeyedDictionary>(ChartPlayer.motionRegistry.Count); foreach (var reg in ChartPlayer.motionRegistry) - m_buckets.Add(reg.Key, new Bucket(reg.Key, 4096)); + m_buckets.Add(reg.Key.Key, new Bucket(reg.Key, 4096)); } } } diff --git a/Assets/Cryville/Crtr/Event/RMVPool.cs b/Assets/Cryville/Crtr/Event/RMVPool.cs index 2591103..910d01d 100644 --- a/Assets/Cryville/Crtr/Event/RMVPool.cs +++ b/Assets/Cryville/Crtr/Event/RMVPool.cs @@ -1,9 +1,10 @@ using Cryville.Common; using Cryville.Common.Buffers; +using Cryville.Common.Collections.Specialized; using System.Collections.Generic; namespace Cryville.Crtr.Event { - internal class RMVPool : CategorizedPool { + internal class RMVPool : CategorizedPool { private class Bucket : ObjectPool { readonly MotionRegistry _reg; public Bucket(Identifier name, int capacity) : base(capacity) { @@ -13,12 +14,12 @@ namespace Cryville.Crtr.Event { return new RealtimeMotionValue().Init(_reg.InitValue); } } - readonly Dictionary> m_buckets; - protected override IReadOnlyDictionary> Buckets { get { return m_buckets; } } + readonly IntKeyedDictionary> m_buckets; + protected override IReadOnlyDictionary> Buckets { get { return m_buckets; } } public RMVPool() { - m_buckets = new Dictionary>(ChartPlayer.motionRegistry.Count); + m_buckets = new IntKeyedDictionary>(ChartPlayer.motionRegistry.Count); foreach (var reg in ChartPlayer.motionRegistry) - m_buckets.Add(reg.Key, new Bucket(reg.Key, 4096)); + m_buckets.Add(reg.Key.Key, new Bucket(reg.Key, 4096)); } } } diff --git a/Assets/Cryville/Crtr/Judge.cs b/Assets/Cryville/Crtr/Judge.cs index 92f622a..e098e3e 100644 --- a/Assets/Cryville/Crtr/Judge.cs +++ b/Assets/Cryville/Crtr/Judge.cs @@ -1,5 +1,6 @@ using Cryville.Common; using Cryville.Common.Buffers; +using Cryville.Common.Collections.Specialized; using Cryville.Common.Pdt; using System; using System.Collections.Generic; @@ -82,7 +83,7 @@ namespace Cryville.Crtr { } #endregion #region Judge - internal readonly Dictionary judgeMap = new Dictionary(); + internal readonly IntKeyedDictionary judgeMap = new IntKeyedDictionary(); void InitJudges() { foreach (var i in _rs.judges) { var id = i.Key; @@ -215,15 +216,15 @@ namespace Cryville.Crtr { } #endregion #region Score - readonly Dictionary scoreStringKeys = new Dictionary(); - readonly Dictionary scoreStringKeysRev = new Dictionary(); - readonly Dictionary scoreSrcs = new Dictionary(); - readonly Dictionary scoreOps = new Dictionary(); - readonly Dictionary scoreDefs = new Dictionary(); - readonly Dictionary scores = new Dictionary(); - readonly Dictionary scoreStringCache = new Dictionary(); + readonly IntKeyedDictionary scoreStringKeys = new IntKeyedDictionary(); + readonly IntKeyedDictionary scoreStringKeysRev = new IntKeyedDictionary(); + readonly IntKeyedDictionary scoreSrcs = new IntKeyedDictionary(); + readonly IntKeyedDictionary scoreOps = new IntKeyedDictionary(); + readonly IntKeyedDictionary scoreDefs = new IntKeyedDictionary(); + readonly IntKeyedDictionary scores = new IntKeyedDictionary(); + readonly IntKeyedDictionary scoreStringCache = new IntKeyedDictionary(); readonly ArrayPool scoreStringPool = new ArrayPool(); - readonly Dictionary scoreFormatCache = new Dictionary(); + readonly IntKeyedDictionary scoreFormatCache = new IntKeyedDictionary(); readonly TargetString scoreFullStr = new TargetString(); readonly StringBuffer scoreFullBuf = new StringBuffer(); void InitScores() { diff --git a/Assets/Cryville/Crtr/PdtEvaluator.cs b/Assets/Cryville/Crtr/PdtEvaluator.cs index ce0aeec..7352206 100644 --- a/Assets/Cryville/Crtr/PdtEvaluator.cs +++ b/Assets/Cryville/Crtr/PdtEvaluator.cs @@ -1,4 +1,5 @@ using Cryville.Common; +using Cryville.Common.Collections.Specialized; using Cryville.Common.Math; using Cryville.Common.Pdt; using Cryville.Crtr.Event; @@ -10,7 +11,7 @@ using UnityEngine; namespace Cryville.Crtr { public class PdtEvaluator : PdtEvaluatorBase { static readonly Dictionary _shortops = new Dictionary(); - readonly Dictionary _ctxops = new Dictionary(); + readonly IntKeyedDictionary _ctxops = new IntKeyedDictionary(); static readonly byte[] _nullbuf = new byte[0]; readonly byte[] _numbuf = new byte[4]; @@ -31,13 +32,12 @@ namespace Cryville.Crtr { else if (name == _var_false) { LoadNum(0); type = PdtInternalType.Number; value = _numbuf; } else if (name == _var_null) { LoadIdent(0); type = PdtInternalType.Undefined; value = _numbuf; } else { - var id = new Identifier(name); PropSrc prop; SkinVariable variable; if (ContextEvent != null && ContextEvent.PropSrcs.TryGetValue(name, out prop)) { prop.Get(out type, out value); } - else if (ContextState != null && ChartPlayer.motionRegistry.ContainsKey(id)) { - _vec = ContextState.GetRawValue(id); + else if (ContextState != null && ChartPlayer.motionRegistry.ContainsKey(new Identifier(name))) { + _vec = ContextState.GetRawValue(name); _vecsrc.Invalidate(); _vecsrc.Get(out type, out value); } @@ -121,12 +121,12 @@ namespace Cryville.Crtr { public void ContextCascadeDiscardBlock() { ContextCascadeBlocks.Pop(); } - readonly Dictionary[] ContextCascade = new Dictionary[256]; + readonly IntKeyedDictionary[] ContextCascade = new IntKeyedDictionary[256]; int _cascadeHeight; public void ContextCascadeInsert() { ContextCascade[_cascadeHeight++].Clear(); } - public void ContextCascadeInsert(Dictionary srcs) { + public void ContextCascadeInsert(IReadOnlyDictionary srcs) { ContextCascadeInsert(); foreach (var src in srcs) ContextCascadeUpdate(src.Key, src.Value); } @@ -136,7 +136,7 @@ namespace Cryville.Crtr { public PropSrc ContextCascadeLookup(int name) { PropSrc result; for (int i = _cascadeHeight - 1; i >= ContextCascadeBlocks.Peek(); i--) { - Dictionary cas = ContextCascade[i]; + var cas = ContextCascade[i]; if (cas.TryGetValue(name, out result)) { return result; } @@ -149,7 +149,7 @@ namespace Cryville.Crtr { public PdtEvaluator() { ContextCascadeBlocks.Push(0); - for (int i = 0; i < ContextCascade.Length; i++) ContextCascade[i] = new Dictionary(); + for (int i = 0; i < ContextCascade.Length; i++) ContextCascade[i] = new IntKeyedDictionary(); _vecsrc = new VectorSrc(() => _vec); _ctxops.Add(IdentifierManager.SharedInstance.Request("screen_edge"), new func_screen_edge(() => ContextTransform)); diff --git a/Assets/Cryville/Crtr/PropOp.cs b/Assets/Cryville/Crtr/PropOp.cs index cb08ad3..3198b95 100644 --- a/Assets/Cryville/Crtr/PropOp.cs +++ b/Assets/Cryville/Crtr/PropOp.cs @@ -1,7 +1,7 @@ using Cryville.Common; +using Cryville.Common.Collections.Specialized; using Cryville.Common.Pdt; using System; -using System.Collections.Generic; using System.Reflection; using RBeatTime = Cryville.Crtr.BeatTime; using RClip = Cryville.Crtr.Clip; @@ -100,7 +100,7 @@ namespace Cryville.Crtr { } } public class Enum : PropOp { - static readonly Dictionary _cache = new Dictionary(); + static readonly IntKeyedDictionary _cache = new IntKeyedDictionary(); readonly Action _cb; readonly Func _caster; static Enum() { @@ -109,7 +109,6 @@ namespace Cryville.Crtr { var names = typeof(T).GetFields(BindingFlags.Public | BindingFlags.Static); for (int i = 0; i < names.Length; i++) _cache[IdentifierManager.SharedInstance.Request(names[i].Name)] = Convert.ToInt32(names[i].GetValue(null)); - } public Enum(Action cb, Func caster) { _cb = cb; diff --git a/Assets/Cryville/Crtr/SkinContainer.cs b/Assets/Cryville/Crtr/SkinContainer.cs index 0af87a4..679a693 100644 --- a/Assets/Cryville/Crtr/SkinContainer.cs +++ b/Assets/Cryville/Crtr/SkinContainer.cs @@ -1,3 +1,4 @@ +using Cryville.Common.Collections.Specialized; using Cryville.Common.Pdt; using System.Collections.Generic; using UnityEngine; @@ -9,7 +10,7 @@ namespace Cryville.Crtr { readonly SkinElement _rootElement; readonly DynamicStack[] _stacks = new DynamicStack[2]; readonly HashSet _once = new HashSet(); - public readonly Dictionary Variables = new Dictionary(); + public readonly IntKeyedDictionary Variables = new IntKeyedDictionary(); class DynamicStack { public readonly List Properties = new List(); @@ -167,8 +168,8 @@ namespace Cryville.Crtr { } public class SkinContext { public Transform Transform { get; private set; } - public Dictionary PropSrcs { get; private set; } - public SkinContext(Transform transform, Dictionary propSrcs = null) { + public IReadOnlyDictionary PropSrcs { get; private set; } + public SkinContext(Transform transform, IReadOnlyDictionary propSrcs = null) { Transform = transform; PropSrcs = propSrcs; } diff --git a/Assets/Cryville/Crtr/SkinPropertyKey.cs b/Assets/Cryville/Crtr/SkinPropertyKey.cs index c6c4a1f..7e61bf7 100644 --- a/Assets/Cryville/Crtr/SkinPropertyKey.cs +++ b/Assets/Cryville/Crtr/SkinPropertyKey.cs @@ -1,4 +1,5 @@ using Cryville.Common; +using Cryville.Common.Collections.Specialized; using Cryville.Common.Pdt; using Cryville.Crtr.Components; using System; @@ -50,8 +51,8 @@ namespace Cryville.Crtr { } public abstract override string ToString(); public abstract bool IsValueRequired { get; } - public abstract void ExecuteStatic(ISkinnableGroup group, RuntimeSkinContext ctx, PdtExpression exp, Dictionary vars); - public abstract void ExecuteDynamic(ISkinnableGroup group, RuntimeSkinContext ctx, PdtExpression exp, Dictionary vars, int dl); + public abstract void ExecuteStatic(ISkinnableGroup group, RuntimeSkinContext ctx, PdtExpression exp, IntKeyedDictionary vars); + public abstract void ExecuteDynamic(ISkinnableGroup group, RuntimeSkinContext ctx, PdtExpression exp, IntKeyedDictionary vars, int dl); public class CreateComponent : SkinPropertyKey { public Type Component { get; private set; } public CreateComponent(IEnumerable a, Type component) : base(a) { @@ -61,10 +62,10 @@ namespace Cryville.Crtr { return string.Format("*{0}", Component.Name); } public override bool IsValueRequired { get { return false; } } - public override void ExecuteStatic(ISkinnableGroup group, RuntimeSkinContext ctx, PdtExpression exp, Dictionary vars) { + public override void ExecuteStatic(ISkinnableGroup group, RuntimeSkinContext ctx, PdtExpression exp, IntKeyedDictionary vars) { ctx.WriteTransform.gameObject.AddComponent(Component); } - public override void ExecuteDynamic(ISkinnableGroup group, RuntimeSkinContext ctx, PdtExpression exp, Dictionary vars, int dl) { + public override void ExecuteDynamic(ISkinnableGroup group, RuntimeSkinContext ctx, PdtExpression exp, IntKeyedDictionary vars, int dl) { throw new InvalidOperationException("Component creation in dynamic context is not allowed"); } } @@ -79,10 +80,10 @@ namespace Cryville.Crtr { return string.Format("{0}.{1}", Component.Name, IdentifierManager.SharedInstance.Retrieve(Name)); } public override bool IsValueRequired { get { return true; } } - public override void ExecuteStatic(ISkinnableGroup group, RuntimeSkinContext ctx, PdtExpression exp, Dictionary vars) { + public override void ExecuteStatic(ISkinnableGroup group, RuntimeSkinContext ctx, PdtExpression exp, IntKeyedDictionary vars) { Execute(ctx, GetPropOp(ctx.WriteTransform).Operator, exp); } - public override void ExecuteDynamic(ISkinnableGroup group, RuntimeSkinContext ctx, PdtExpression exp, Dictionary vars, int dl) { + public override void ExecuteDynamic(ISkinnableGroup group, RuntimeSkinContext ctx, PdtExpression exp, IntKeyedDictionary vars, int dl) { var prop = GetPropOp(ctx.WriteTransform); if (dl > prop.UpdateDynamicLevel) return; Execute(ctx, prop.Operator, exp); @@ -118,10 +119,10 @@ namespace Cryville.Crtr { return string.Format("@has {0}", IdentifierManager.SharedInstance.Retrieve(Name)); } public override bool IsValueRequired { get { return false; } } - public override void ExecuteStatic(ISkinnableGroup group, RuntimeSkinContext ctx, PdtExpression exp, Dictionary vars) { + public override void ExecuteStatic(ISkinnableGroup group, RuntimeSkinContext ctx, PdtExpression exp, IntKeyedDictionary vars) { group.RegisterAnchor(Name); } - public override void ExecuteDynamic(ISkinnableGroup group, RuntimeSkinContext ctx, PdtExpression exp, Dictionary vars, int dl) { + public override void ExecuteDynamic(ISkinnableGroup group, RuntimeSkinContext ctx, PdtExpression exp, IntKeyedDictionary vars, int dl) { throw new InvalidOperationException("Anchor creation in dynamic context is not allowed"); } } @@ -135,12 +136,12 @@ namespace Cryville.Crtr { return string.Format("@at {0}", IdentifierManager.SharedInstance.Retrieve(Name)); } public override bool IsValueRequired { get { return true; } } - public override void ExecuteStatic(ISkinnableGroup group, RuntimeSkinContext ctx, PdtExpression exp, Dictionary vars) { + public override void ExecuteStatic(ISkinnableGroup group, RuntimeSkinContext ctx, PdtExpression exp, IntKeyedDictionary vars) { throw new InvalidOperationException("Setting anchor in static context is not allowed"); } float _time; readonly PropOp _timeOp; - public override void ExecuteDynamic(ISkinnableGroup group, RuntimeSkinContext ctx, PdtExpression exp, Dictionary vars, int dl) { + public override void ExecuteDynamic(ISkinnableGroup group, RuntimeSkinContext ctx, PdtExpression exp, IntKeyedDictionary vars, int dl) { if (dl > 0) return; var psrcs = ctx.ReadContext.PropSrcs; if (psrcs != null) ChartPlayer.etor.ContextCascadeInsert(psrcs); @@ -161,12 +162,12 @@ namespace Cryville.Crtr { return string.Format(IsSelf ? "@emit_self {0}" : "@emit {0}", IdentifierManager.SharedInstance.Retrieve(Name)); } public override bool IsValueRequired { get { return true; } } - public override void ExecuteStatic(ISkinnableGroup group, RuntimeSkinContext ctx, PdtExpression exp, Dictionary vars) { + public override void ExecuteStatic(ISkinnableGroup group, RuntimeSkinContext ctx, PdtExpression exp, IntKeyedDictionary vars) { throw new InvalidOperationException("Emitting effect in static context is not allowed"); } float _index; readonly PropOp _op; - public override void ExecuteDynamic(ISkinnableGroup group, RuntimeSkinContext ctx, PdtExpression exp, Dictionary vars, int dl) { + public override void ExecuteDynamic(ISkinnableGroup group, RuntimeSkinContext ctx, PdtExpression exp, IntKeyedDictionary vars, int dl) { ChartPlayer.etor.Evaluate(_op, exp); if (IsSelf) ChartPlayer.effectManager.EmitSelf(Name, _index, ctx.WriteTransform); else ChartPlayer.effectManager.Emit(Name, _index); @@ -181,13 +182,13 @@ namespace Cryville.Crtr { return string.Format("@var {0}", IdentifierManager.SharedInstance.Retrieve(Name)); } public override bool IsValueRequired { get { return true; } } - public override void ExecuteStatic(ISkinnableGroup group, RuntimeSkinContext ctx, PdtExpression exp, Dictionary vars) { + public override void ExecuteStatic(ISkinnableGroup group, RuntimeSkinContext ctx, PdtExpression exp, IntKeyedDictionary vars) { SkinVariable v; if (!vars.TryGetValue(Name, out v)) vars.Add(Name, v = new SkinVariable()); ChartPlayer.etor.Evaluate(v.Op, exp); } - public override void ExecuteDynamic(ISkinnableGroup group, RuntimeSkinContext ctx, PdtExpression exp, Dictionary vars, int dl) { + public override void ExecuteDynamic(ISkinnableGroup group, RuntimeSkinContext ctx, PdtExpression exp, IntKeyedDictionary vars, int dl) { SkinVariable v; if (!vars.TryGetValue(Name, out v)) throw new InvalidOperationException(string.Format("Variable \"{0}\" not defined", IdentifierManager.SharedInstance.Retrieve(Name)));