From 5d9ec1887700814152d89a0e01efe99709303cfd Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Sun, 18 Jul 2021 21:00:01 -0700 Subject: [PATCH] Ensure consistency by using ConcurrentList (fixes #36) --- RombaSharp/Features/Archive.cs | 2 +- RombaSharp/Features/RescanDepots.cs | 2 +- SabreTools.Core/ConcurrentList.cs | 160 ++++++++++++++++++ SabreTools.Core/ConcurrentListExtensions.cs | 15 ++ SabreTools.DatFiles/DatFile.cs | 2 +- SabreTools.DatFiles/Formats/AttractMode.cs | 3 +- SabreTools.DatFiles/Formats/ClrMamePro.cs | 3 +- SabreTools.DatFiles/Formats/DosCenter.cs | 2 +- SabreTools.DatFiles/Formats/EverdriveSmdb.cs | 3 +- SabreTools.DatFiles/Formats/Hashfile.cs | 3 +- SabreTools.DatFiles/Formats/Listrom.cs | 3 +- SabreTools.DatFiles/Formats/Listxml.cs | 2 +- SabreTools.DatFiles/Formats/Logiqx.cs | 2 +- SabreTools.DatFiles/Formats/Missfile.cs | 4 +- SabreTools.DatFiles/Formats/OfflineList.cs | 2 +- SabreTools.DatFiles/Formats/OpenMSX.cs | 2 +- SabreTools.DatFiles/Formats/RomCenter.cs | 2 +- SabreTools.DatFiles/Formats/SabreJSON.cs | 2 +- SabreTools.DatFiles/Formats/SabreXML.cs | 4 +- SabreTools.DatFiles/Formats/SeparatedValue.cs | 3 +- SabreTools.DatFiles/Formats/SoftwareList.cs | 2 +- SabreTools.DatFiles/ItemDictionary.cs | 81 +++++---- SabreTools.DatItems/DatItem.cs | 12 +- SabreTools.DatTools/DatFileTool.cs | 26 +-- SabreTools.DatTools/Rebuilder.cs | 4 +- SabreTools.DatTools/Splitter.cs | 10 +- SabreTools.DatTools/Verification.cs | 2 +- SabreTools.Filtering/Cleaner.cs | 12 +- SabreTools.Filtering/ExtraIni.cs | 2 +- SabreTools.Filtering/Filter.cs | 2 +- SabreTools.Filtering/Remover.cs | 2 +- SabreTools.Filtering/Splitter.cs | 20 +-- .../DatFiles/ItemDictionaryTests.cs | 15 +- 33 files changed, 289 insertions(+), 122 deletions(-) create mode 100644 SabreTools.Core/ConcurrentList.cs create mode 100644 SabreTools.Core/ConcurrentListExtensions.cs diff --git a/RombaSharp/Features/Archive.cs b/RombaSharp/Features/Archive.cs index 83eb7891..b05c2993 100644 --- a/RombaSharp/Features/Archive.cs +++ b/RombaSharp/Features/Archive.cs @@ -93,7 +93,7 @@ have a current entry in the DAT index."; foreach (string key in df.Items.Keys) { - List datItems = df.Items[key]; + ConcurrentList datItems = df.Items[key]; foreach (Rom rom in datItems) { // If we care about if the file exists, check the databse first diff --git a/RombaSharp/Features/RescanDepots.cs b/RombaSharp/Features/RescanDepots.cs index 4b056566..9537da81 100644 --- a/RombaSharp/Features/RescanDepots.cs +++ b/RombaSharp/Features/RescanDepots.cs @@ -88,7 +88,7 @@ namespace RombaSharp.Features IEnumerable keys = depot.Items.Keys; foreach (string key in keys) { - List roms = depot.Items[key]; + ConcurrentList roms = depot.Items[key]; foreach (Rom rom in roms) { if (hashes.Contains(rom.SHA1)) diff --git a/SabreTools.Core/ConcurrentList.cs b/SabreTools.Core/ConcurrentList.cs new file mode 100644 index 00000000..e3dd1da3 --- /dev/null +++ b/SabreTools.Core/ConcurrentList.cs @@ -0,0 +1,160 @@ +using System; +using System.Collections; +using System.Collections.Generic; + +namespace SabreTools.Core +{ + /// + /// Thread-safe list class + /// + public class ConcurrentList : ICollection, IEnumerable, IEnumerable, IList, IReadOnlyCollection, IReadOnlyList, ICollection, IList + { + private List _list = new List(); + private object _lock = new object(); + + public T this[int index] + { + get { lock (_lock) return _list[index]; } + set { lock (_lock) _list[index] = value; } + } + + object IList.this[int index] + { + get { lock (_lock) return ((IList)_list)[index]; } + set { lock (_lock) ((IList)_list)[index] = value; } + } + + public int Count + { + get { lock (_lock) return _list.Count; } + } + + public bool IsFixedSize => ((IList)_list).IsFixedSize; + + public bool IsReadOnly => ((IList)_list).IsReadOnly; + + public bool IsSynchronized => ((ICollection)_list).IsSynchronized; + + public object SyncRoot => ((ICollection)_list).SyncRoot; + + public void Add(T item) + { + lock (_lock) + _list.Add(item); + } + + public int Add(object value) + { + lock (_lock) + return ((IList)_list).Add(value); + } + + public void AddRange(IEnumerable values) + { + lock (_lock) + _list.AddRange(values); + } + + public void Clear() + { + lock (_lock) + _list.Clear(); + } + + public bool Contains(T item) + { + lock (_lock) + return _list.Contains(item); + } + + public bool Contains(object value) + { + lock (_lock) + return ((IList)_list).Contains(value); + } + + public void CopyTo(T[] array, int arrayIndex) + { + lock (_lock) + _list.CopyTo(array, arrayIndex); + } + + public void CopyTo(Array array, int index) + { + lock (_lock) + ((ICollection)_list).CopyTo(array, index); + } + + public void ForEach(Action action) + { + lock (_lock) + _list.ForEach(action); + } + + public IEnumerator GetEnumerator() + { + lock (_lock) + return _list.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + lock (_lock) + return ((IEnumerable)_list).GetEnumerator(); + } + + public int IndexOf(T item) + { + lock (_lock) + return _list.IndexOf(item); + } + + public int IndexOf(object value) + { + lock (_lock) + return ((IList)_list).IndexOf(value); + } + + public void Insert(int index, T item) + { + lock (_lock) + _list.Insert(index, item); + } + + public void Insert(int index, object value) + { + lock (_lock) + ((IList)_list).Insert(index, value); + } + + public bool Remove(T item) + { + lock (_lock) + return _list.Remove(item); + } + + public void Remove(object value) + { + lock (_lock) + ((IList)_list).Remove(value); + } + + public void RemoveAt(int index) + { + lock (_lock) + _list.RemoveAt(index); + } + + public void SetInternalList(List list) + { + lock (_lock) + _list = list; + } + + public void Sort(Comparison comparison) + { + lock (_lock) + _list.Sort(comparison); + } + } +} diff --git a/SabreTools.Core/ConcurrentListExtensions.cs b/SabreTools.Core/ConcurrentListExtensions.cs new file mode 100644 index 00000000..4150bdb8 --- /dev/null +++ b/SabreTools.Core/ConcurrentListExtensions.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; +using System.Linq; + +namespace SabreTools.Core +{ + public static class ConcurrentListExtensions + { + public static ConcurrentList ToConcurrentList(this IEnumerable values) + { + var list = new ConcurrentList(); + list.SetInternalList(values.ToList()); + return list; + } + } +} diff --git a/SabreTools.DatFiles/DatFile.cs b/SabreTools.DatFiles/DatFile.cs index 5a32cef5..1522d604 100644 --- a/SabreTools.DatFiles/DatFile.cs +++ b/SabreTools.DatFiles/DatFile.cs @@ -513,7 +513,7 @@ namespace SabreTools.DatFiles /// DatItems to check /// True if the machine contains at least one writable item, false otherwise /// Empty machines are kept with this - protected bool ContainsWritable(List datItems) + protected bool ContainsWritable(ConcurrentList datItems) { // Empty machines are considered writable if (datItems == null || datItems.Count == 0) diff --git a/SabreTools.DatFiles/Formats/AttractMode.cs b/SabreTools.DatFiles/Formats/AttractMode.cs index 5485e0ff..95286c0c 100644 --- a/SabreTools.DatFiles/Formats/AttractMode.cs +++ b/SabreTools.DatFiles/Formats/AttractMode.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.IO; using System.Text; @@ -141,7 +140,7 @@ namespace SabreTools.DatFiles.Formats // Use a sorted list of games to output foreach (string key in Items.SortedKeys) { - List datItems = Items.FilteredItems(key); + ConcurrentList datItems = Items.FilteredItems(key); // If this machine doesn't contain any writable items, skip if (!ContainsWritable(datItems)) diff --git a/SabreTools.DatFiles/Formats/ClrMamePro.cs b/SabreTools.DatFiles/Formats/ClrMamePro.cs index 666b4f6a..1c66153e 100644 --- a/SabreTools.DatFiles/Formats/ClrMamePro.cs +++ b/SabreTools.DatFiles/Formats/ClrMamePro.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.IO; using System.Text; @@ -463,7 +462,7 @@ namespace SabreTools.DatFiles.Formats // Use a sorted list of games to output foreach (string key in Items.SortedKeys) { - List datItems = Items.FilteredItems(key); + ConcurrentList datItems = Items.FilteredItems(key); // If this machine doesn't contain any writable items, skip if (!ContainsWritable(datItems)) diff --git a/SabreTools.DatFiles/Formats/DosCenter.cs b/SabreTools.DatFiles/Formats/DosCenter.cs index eea572cd..5808cb0d 100644 --- a/SabreTools.DatFiles/Formats/DosCenter.cs +++ b/SabreTools.DatFiles/Formats/DosCenter.cs @@ -288,7 +288,7 @@ namespace SabreTools.DatFiles.Formats // Use a sorted list of games to output foreach (string key in Items.SortedKeys) { - List datItems = Items.FilteredItems(key); + ConcurrentList datItems = Items.FilteredItems(key); // If this machine doesn't contain any writable items, skip if (!ContainsWritable(datItems)) diff --git a/SabreTools.DatFiles/Formats/EverdriveSmdb.cs b/SabreTools.DatFiles/Formats/EverdriveSmdb.cs index 8b17bb87..f8510d06 100644 --- a/SabreTools.DatFiles/Formats/EverdriveSmdb.cs +++ b/SabreTools.DatFiles/Formats/EverdriveSmdb.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.IO; using System.Text; @@ -135,7 +134,7 @@ namespace SabreTools.DatFiles.Formats // Use a sorted list of games to output foreach (string key in Items.SortedKeys) { - List datItems = Items.FilteredItems(key); + ConcurrentList datItems = Items.FilteredItems(key); // If this machine doesn't contain any writable items, skip if (!ContainsWritable(datItems)) diff --git a/SabreTools.DatFiles/Formats/Hashfile.cs b/SabreTools.DatFiles/Formats/Hashfile.cs index 94a4a8d5..dde234b1 100644 --- a/SabreTools.DatFiles/Formats/Hashfile.cs +++ b/SabreTools.DatFiles/Formats/Hashfile.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.IO; using System.Text; @@ -130,7 +129,7 @@ namespace SabreTools.DatFiles.Formats // Use a sorted list of games to output foreach (string key in Items.SortedKeys) { - List datItems = Items[key]; + ConcurrentList datItems = Items[key]; // If this machine doesn't contain any writable items, skip if (!ContainsWritable(datItems)) diff --git a/SabreTools.DatFiles/Formats/Listrom.cs b/SabreTools.DatFiles/Formats/Listrom.cs index af40faf8..232530dd 100644 --- a/SabreTools.DatFiles/Formats/Listrom.cs +++ b/SabreTools.DatFiles/Formats/Listrom.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.IO; using System.Text; using System.Text.RegularExpressions; @@ -282,7 +281,7 @@ namespace SabreTools.DatFiles.Formats // Use a sorted list of games to output foreach (string key in Items.SortedKeys) { - List datItems = Items.FilteredItems(key); + ConcurrentList datItems = Items.FilteredItems(key); // If this machine doesn't contain any writable items, skip if (!ContainsWritable(datItems)) diff --git a/SabreTools.DatFiles/Formats/Listxml.cs b/SabreTools.DatFiles/Formats/Listxml.cs index ee63deab..319f9a16 100644 --- a/SabreTools.DatFiles/Formats/Listxml.cs +++ b/SabreTools.DatFiles/Formats/Listxml.cs @@ -1372,7 +1372,7 @@ namespace SabreTools.DatFiles.Formats // Use a sorted list of games to output foreach (string key in Items.SortedKeys) { - List datItems = Items.FilteredItems(key); + ConcurrentList datItems = Items.FilteredItems(key); // If this machine doesn't contain any writable items, skip if (!ContainsWritable(datItems)) diff --git a/SabreTools.DatFiles/Formats/Logiqx.cs b/SabreTools.DatFiles/Formats/Logiqx.cs index ab5352c3..acc5231a 100644 --- a/SabreTools.DatFiles/Formats/Logiqx.cs +++ b/SabreTools.DatFiles/Formats/Logiqx.cs @@ -804,7 +804,7 @@ namespace SabreTools.DatFiles.Formats // Use a sorted list of games to output foreach (string key in Items.SortedKeys) { - List datItems = Items.FilteredItems(key); + ConcurrentList datItems = Items.FilteredItems(key); // If this machine doesn't contain any writable items, skip if (!ContainsWritable(datItems)) diff --git a/SabreTools.DatFiles/Formats/Missfile.cs b/SabreTools.DatFiles/Formats/Missfile.cs index 3de180e8..06b82e98 100644 --- a/SabreTools.DatFiles/Formats/Missfile.cs +++ b/SabreTools.DatFiles/Formats/Missfile.cs @@ -1,8 +1,8 @@ using System; -using System.Collections.Generic; using System.IO; using System.Text; +using SabreTools.Core; using SabreTools.DatItems; namespace SabreTools.DatFiles.Formats @@ -51,7 +51,7 @@ namespace SabreTools.DatFiles.Formats // Use a sorted list of games to output foreach (string key in Items.SortedKeys) { - List datItems = Items.FilteredItems(key); + ConcurrentList datItems = Items.FilteredItems(key); // If this machine doesn't contain any writable items, skip if (!ContainsWritable(datItems)) diff --git a/SabreTools.DatFiles/Formats/OfflineList.cs b/SabreTools.DatFiles/Formats/OfflineList.cs index d486f91a..9afa71fd 100644 --- a/SabreTools.DatFiles/Formats/OfflineList.cs +++ b/SabreTools.DatFiles/Formats/OfflineList.cs @@ -693,7 +693,7 @@ namespace SabreTools.DatFiles.Formats // Use a sorted list of games to output foreach (string key in Items.SortedKeys) { - List datItems = Items.FilteredItems(key); + ConcurrentList datItems = Items.FilteredItems(key); // If this machine doesn't contain any writable items, skip if (!ContainsWritable(datItems)) diff --git a/SabreTools.DatFiles/Formats/OpenMSX.cs b/SabreTools.DatFiles/Formats/OpenMSX.cs index 5e996c47..081bb772 100644 --- a/SabreTools.DatFiles/Formats/OpenMSX.cs +++ b/SabreTools.DatFiles/Formats/OpenMSX.cs @@ -566,7 +566,7 @@ namespace SabreTools.DatFiles.Formats // Use a sorted list of games to output foreach (string key in Items.SortedKeys) { - List datItems = Items.FilteredItems(key); + ConcurrentList datItems = Items.FilteredItems(key); // If this machine doesn't contain any writable items, skip if (!ContainsWritable(datItems)) diff --git a/SabreTools.DatFiles/Formats/RomCenter.cs b/SabreTools.DatFiles/Formats/RomCenter.cs index 32c4bda3..ff962e49 100644 --- a/SabreTools.DatFiles/Formats/RomCenter.cs +++ b/SabreTools.DatFiles/Formats/RomCenter.cs @@ -391,7 +391,7 @@ namespace SabreTools.DatFiles.Formats // Use a sorted list of games to output foreach (string key in Items.SortedKeys) { - List datItems = Items.FilteredItems(key); + ConcurrentList datItems = Items.FilteredItems(key); // If this machine doesn't contain any writable items, skip if (!ContainsWritable(datItems)) diff --git a/SabreTools.DatFiles/Formats/SabreJSON.cs b/SabreTools.DatFiles/Formats/SabreJSON.cs index 99cd2b4b..9ed7a8c1 100644 --- a/SabreTools.DatFiles/Formats/SabreJSON.cs +++ b/SabreTools.DatFiles/Formats/SabreJSON.cs @@ -364,7 +364,7 @@ namespace SabreTools.DatFiles.Formats // Use a sorted list of games to output foreach (string key in Items.SortedKeys) { - List datItems = Items.FilteredItems(key); + ConcurrentList datItems = Items.FilteredItems(key); // If this machine doesn't contain any writable items, skip if (!ContainsWritable(datItems)) diff --git a/SabreTools.DatFiles/Formats/SabreXML.cs b/SabreTools.DatFiles/Formats/SabreXML.cs index 928f4b5b..c1ec5d70 100644 --- a/SabreTools.DatFiles/Formats/SabreXML.cs +++ b/SabreTools.DatFiles/Formats/SabreXML.cs @@ -1,11 +1,11 @@ using System; -using System.Collections.Generic; using System.IO; using System.Text; using System.Xml; using System.Xml.Schema; using System.Xml.Serialization; +using SabreTools.Core; using SabreTools.DatItems; namespace SabreTools.DatFiles.Formats @@ -208,7 +208,7 @@ namespace SabreTools.DatFiles.Formats // Use a sorted list of games to output foreach (string key in Items.SortedKeys) { - List datItems = Items.FilteredItems(key); + ConcurrentList datItems = Items.FilteredItems(key); // If this machine doesn't contain any writable items, skip if (!ContainsWritable(datItems)) diff --git a/SabreTools.DatFiles/Formats/SeparatedValue.cs b/SabreTools.DatFiles/Formats/SeparatedValue.cs index ac8af43e..06110a20 100644 --- a/SabreTools.DatFiles/Formats/SeparatedValue.cs +++ b/SabreTools.DatFiles/Formats/SeparatedValue.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.IO; using System.Text; @@ -124,7 +123,7 @@ namespace SabreTools.DatFiles.Formats // Use a sorted list of games to output foreach (string key in Items.SortedKeys) { - List datItems = Items.FilteredItems(key); + ConcurrentList datItems = Items.FilteredItems(key); // If this machine doesn't contain any writable items, skip if (!ContainsWritable(datItems)) diff --git a/SabreTools.DatFiles/Formats/SoftwareList.cs b/SabreTools.DatFiles/Formats/SoftwareList.cs index 69e5498a..14de25ee 100644 --- a/SabreTools.DatFiles/Formats/SoftwareList.cs +++ b/SabreTools.DatFiles/Formats/SoftwareList.cs @@ -613,7 +613,7 @@ namespace SabreTools.DatFiles.Formats // Use a sorted list of games to output foreach (string key in Items.SortedKeys) { - List datItems = Items.FilteredItems(key); + ConcurrentList datItems = Items.FilteredItems(key); // If this machine doesn't contain any writable items, skip if (!ContainsWritable(datItems)) diff --git a/SabreTools.DatFiles/ItemDictionary.cs b/SabreTools.DatFiles/ItemDictionary.cs index dcba995c..7c075555 100644 --- a/SabreTools.DatFiles/ItemDictionary.cs +++ b/SabreTools.DatFiles/ItemDictionary.cs @@ -18,7 +18,7 @@ namespace SabreTools.DatFiles /// Item dictionary with statistics, bucketing, and sorting /// [JsonObject("items"), XmlRoot("items")] - public class ItemDictionary : IDictionary> + public class ItemDictionary : IDictionary> { #region Private instance variables @@ -35,7 +35,7 @@ namespace SabreTools.DatFiles /// /// Internal dictionary for the class /// - private readonly ConcurrentDictionary> items; + private readonly ConcurrentDictionary> items; /// /// Lock for statistics calculation @@ -363,7 +363,7 @@ namespace SabreTools.DatFiles /// Passthrough to access the file dictionary /// /// Key in the dictionary to reference - public List this[string key] + public ConcurrentList this[string key] { get { @@ -417,7 +417,7 @@ namespace SabreTools.DatFiles /// /// Key in the dictionary to add to /// Value to add to the dictionary - public void Add(string key, List value) + public void Add(string key, ConcurrentList value) { AddRange(key, value); } @@ -569,7 +569,7 @@ namespace SabreTools.DatFiles /// /// Key in the dictionary to add to /// Value to add to the dictionary - public void AddRange(string key, List value) + public void AddRange(string key, ConcurrentList value) { // Explicit lock for some weird corner cases lock (key) @@ -678,30 +678,27 @@ namespace SabreTools.DatFiles { // If the key is missing from the dictionary, add it if (!items.ContainsKey(key)) - items.TryAdd(key, new List()); + items.TryAdd(key, new ConcurrentList()); } /// /// Get a list of filtered items for a given key /// /// Key in the dictionary to retrieve - public List FilteredItems(string key) + public ConcurrentList FilteredItems(string key) { lock (key) { // Get the list, if possible - List fi = items[key]; + ConcurrentList fi = items[key]; if (fi == null) - return new List(); + return new ConcurrentList(); // Filter the list - fi = fi.Where(i => i != null) + return fi.Where(i => i != null) .Where(i => !i.Remove) .Where(i => i.Machine?.Name != null) - .ToList(); - - // Return the list - return fi; + .ToConcurrentList(); } } @@ -767,7 +764,7 @@ namespace SabreTools.DatFiles } // Remove the key from the dictionary - items[key] = new List(); + items[key] = new ConcurrentList(); return true; } @@ -935,7 +932,7 @@ namespace SabreTools.DatFiles { bucketedBy = ItemKey.NULL; mergedBy = DedupeType.None; - items = new ConcurrentDictionary>(); + items = new ConcurrentDictionary>(); logger = new Logger(this); } @@ -1010,7 +1007,7 @@ namespace SabreTools.DatFiles Parallel.ForEach(keys, Globals.ParallelOptions, key => { // Get the possibly unsorted list - List sortedlist = this[key].ToList(); + ConcurrentList sortedlist = this[key].ToConcurrentList(); // Sort the list of items to be consistent DatItem.Sort(ref sortedlist, false); @@ -1031,7 +1028,7 @@ namespace SabreTools.DatFiles Parallel.ForEach(keys, Globals.ParallelOptions, key => { // Get the possibly unsorted list - List sortedlist = this[key]; + ConcurrentList sortedlist = this[key]; // Sort the list of items to be consistent DatItem.Sort(ref sortedlist, false); @@ -1069,8 +1066,8 @@ namespace SabreTools.DatFiles var keys = items.Keys.ToList(); foreach (string key in keys) { - List oldItemList = items[key]; - List newItemList = oldItemList.Where(i => !i.Remove).ToList(); + ConcurrentList oldItemList = items[key]; + ConcurrentList newItemList = oldItemList.Where(i => !i.Remove).ToConcurrentList(); Remove(key); AddRange(key, newItemList); @@ -1083,9 +1080,9 @@ namespace SabreTools.DatFiles /// Item to try to match /// True if the DAT is already sorted accordingly, false otherwise (default) /// List of matched DatItem objects - public List GetDuplicates(DatItem datItem, bool sorted = false) + public ConcurrentList GetDuplicates(DatItem datItem, bool sorted = false) { - List output = new List(); + ConcurrentList output = new ConcurrentList(); // Check for an empty rom list first if (TotalCount == 0) @@ -1099,8 +1096,8 @@ namespace SabreTools.DatFiles return output; // Try to find duplicates - List roms = this[key]; - List left = new List(); + ConcurrentList roms = this[key]; + ConcurrentList left = new ConcurrentList(); for (int i = 0; i < roms.Count; i++) { DatItem other = roms[i]; @@ -1146,7 +1143,7 @@ namespace SabreTools.DatFiles return false; // Try to find duplicates - List roms = this[key]; + ConcurrentList roms = this[key]; return roms.Any(r => datItem.Equals(r)); } @@ -1165,7 +1162,7 @@ namespace SabreTools.DatFiles // Loop through and add foreach (string key in items.Keys) { - List datItems = items[key]; + ConcurrentList datItems = items[key]; foreach (DatItem item in datItems) { AddItemStatistics(item); @@ -1258,45 +1255,45 @@ namespace SabreTools.DatFiles #region IDictionary Implementations - public ICollection> Values => ((IDictionary>)items).Values; + public ICollection> Values => ((IDictionary>)items).Values; - public int Count => ((ICollection>>)items).Count; + public int Count => ((ICollection>>)items).Count; - public bool IsReadOnly => ((ICollection>>)items).IsReadOnly; + public bool IsReadOnly => ((ICollection>>)items).IsReadOnly; - public bool TryGetValue(string key, out List value) + public bool TryGetValue(string key, out ConcurrentList value) { - return ((IDictionary>)items).TryGetValue(key, out value); + return ((IDictionary>)items).TryGetValue(key, out value); } - public void Add(KeyValuePair> item) + public void Add(KeyValuePair> item) { - ((ICollection>>)items).Add(item); + ((ICollection>>)items).Add(item); } public void Clear() { - ((ICollection>>)items).Clear(); + ((ICollection>>)items).Clear(); } - public bool Contains(KeyValuePair> item) + public bool Contains(KeyValuePair> item) { - return ((ICollection>>)items).Contains(item); + return ((ICollection>>)items).Contains(item); } - public void CopyTo(KeyValuePair>[] array, int arrayIndex) + public void CopyTo(KeyValuePair>[] array, int arrayIndex) { - ((ICollection>>)items).CopyTo(array, arrayIndex); + ((ICollection>>)items).CopyTo(array, arrayIndex); } - public bool Remove(KeyValuePair> item) + public bool Remove(KeyValuePair> item) { - return ((ICollection>>)items).Remove(item); + return ((ICollection>>)items).Remove(item); } - public IEnumerator>> GetEnumerator() + public IEnumerator>> GetEnumerator() { - return ((IEnumerable>>)items).GetEnumerator(); + return ((IEnumerable>>)items).GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() diff --git a/SabreTools.DatItems/DatItem.cs b/SabreTools.DatItems/DatItem.cs index a875e791..2e42a7a5 100644 --- a/SabreTools.DatItems/DatItem.cs +++ b/SabreTools.DatItems/DatItem.cs @@ -526,14 +526,14 @@ namespace SabreTools.DatItems /// /// List of File objects representing the roms to be merged /// A List of DatItem objects representing the merged roms - public static List Merge(List infiles) + public static ConcurrentList Merge(ConcurrentList infiles) { // Check for null or blank roms first if (infiles == null || infiles.Count == 0) - return new List(); + return new ConcurrentList(); // Create output list - List outfiles = new List(); + ConcurrentList outfiles = new ConcurrentList(); // Then deduplicate them by checking to see if data matches previous saved roms int nodumpCount = 0; @@ -637,10 +637,10 @@ namespace SabreTools.DatItems /// /// List of File objects representing the roms to be merged /// A List of DatItem objects representing the renamed roms - public static List ResolveNames(List infiles) + public static ConcurrentList ResolveNames(ConcurrentList infiles) { // Create the output list - List output = new List(); + ConcurrentList output = new ConcurrentList(); // First we want to make sure the list is in alphabetical order Sort(ref infiles, true); @@ -742,7 +742,7 @@ namespace SabreTools.DatItems /// List of File objects representing the roms to be sorted /// True if files are not renamed, false otherwise /// True if it sorted correctly, false otherwise - public static bool Sort(ref List roms, bool norename) + public static bool Sort(ref ConcurrentList roms, bool norename) { roms.Sort(delegate (DatItem x, DatItem y) { diff --git a/SabreTools.DatTools/DatFileTool.cs b/SabreTools.DatTools/DatFileTool.cs index b4f06231..39dcb080 100644 --- a/SabreTools.DatTools/DatFileTool.cs +++ b/SabreTools.DatTools/DatFileTool.cs @@ -36,8 +36,8 @@ namespace SabreTools.DatTools List keys = datFile.Items.Keys.ToList(); Parallel.ForEach(keys, Globals.ParallelOptions, key => { - List items = datFile.Items[key].ToList(); - List newItems = new List(); + ConcurrentList items = datFile.Items[key]; + ConcurrentList newItems = new ConcurrentList(); foreach (DatItem item in items) { DatItem newItem = item; @@ -87,11 +87,11 @@ namespace SabreTools.DatTools // Then we do a hashwise comparison against the base DAT Parallel.ForEach(intDat.Items.Keys, Globals.ParallelOptions, key => { - List datItems = intDat.Items[key]; - List newDatItems = new List(); + ConcurrentList datItems = intDat.Items[key]; + ConcurrentList newDatItems = new ConcurrentList(); foreach (DatItem datItem in datItems) { - List dupes = datFile.Items.GetDuplicates(datItem, sorted: true); + ConcurrentList dupes = datFile.Items.GetDuplicates(datItem, sorted: true); DatItem newDatItem = datItem.Clone() as DatItem; // Replace fields from the first duplicate, if we have one @@ -117,8 +117,8 @@ namespace SabreTools.DatTools // Then we do a namewise comparison against the base DAT Parallel.ForEach(intDat.Items.Keys, Globals.ParallelOptions, key => { - List datItems = intDat.Items[key]; - List newDatItems = new List(); + ConcurrentList datItems = intDat.Items[key]; + ConcurrentList newDatItems = new ConcurrentList(); foreach (DatItem datItem in datItems) { DatItem newDatItem = datItem.Clone() as DatItem; @@ -194,8 +194,8 @@ namespace SabreTools.DatTools // Standard Against uses hashes else { - List datItems = intDat.Items[key]; - List keepDatItems = new List(); + ConcurrentList datItems = intDat.Items[key]; + ConcurrentList keepDatItems = new ConcurrentList(); foreach (DatItem datItem in datItems) { if (!datFile.Items.HasDuplicates(datItem, true)) @@ -288,7 +288,7 @@ namespace SabreTools.DatTools Parallel.ForEach(datFile.Items.Keys, Globals.ParallelOptions, key => { - List items = DatItem.Merge(datFile.Items[key]); + ConcurrentList items = DatItem.Merge(datFile.Items[key]); // If the rom list is empty or null, just skip it if (items == null || items.Count == 0) @@ -366,7 +366,7 @@ namespace SabreTools.DatTools Parallel.ForEach(datFile.Items.Keys, Globals.ParallelOptions, key => { - List items = DatItem.Merge(datFile.Items[key]); + ConcurrentList items = DatItem.Merge(datFile.Items[key]); // If the rom list is empty or null, just skip it if (items == null || items.Count == 0) @@ -429,7 +429,7 @@ namespace SabreTools.DatTools Parallel.ForEach(datFile.Items.Keys, Globals.ParallelOptions, key => { - List items = DatItem.Merge(datFile.Items[key]); + ConcurrentList items = DatItem.Merge(datFile.Items[key]); // If the rom list is empty or null, just skip it if (items == null || items.Count == 0) @@ -534,7 +534,7 @@ namespace SabreTools.DatTools // Loop through and add the items for this index to the output Parallel.ForEach(datFile.Items.Keys, Globals.ParallelOptions, key => { - List items = DatItem.Merge(datFile.Items[key]); + ConcurrentList items = DatItem.Merge(datFile.Items[key]); // If the rom list is empty or null, just skip it if (items == null || items.Count == 0) diff --git a/SabreTools.DatTools/Rebuilder.cs b/SabreTools.DatTools/Rebuilder.cs index dc915707..58540470 100644 --- a/SabreTools.DatTools/Rebuilder.cs +++ b/SabreTools.DatTools/Rebuilder.cs @@ -375,7 +375,7 @@ namespace SabreTools.DatTools return false; // If either we have duplicates or we're filtering - if (ShouldRebuild(datFile, datItem, fileStream, inverse, out List dupes)) + if (ShouldRebuild(datFile, datItem, fileStream, inverse, out ConcurrentList dupes)) { // If we have a very specific TGZ->TGZ case, just copy it accordingly if (RebuildTorrentGzip(datFile, datItem, file, outDir, outputFormat, isZip)) @@ -480,7 +480,7 @@ namespace SabreTools.DatTools /// True if the DAT should be used as a filter instead of a template, false otherwise /// Output list of duplicate items to rebuild to /// True if the item should be rebuilt, false otherwise - private static bool ShouldRebuild(DatFile datFile, DatItem datItem, Stream stream, bool inverse, out List dupes) + private static bool ShouldRebuild(DatFile datFile, DatItem datItem, Stream stream, bool inverse, out ConcurrentList dupes) { // Find if the file has duplicates in the DAT dupes = datFile.Items.GetDuplicates(datItem); diff --git a/SabreTools.DatTools/Splitter.cs b/SabreTools.DatTools/Splitter.cs index 78127c03..eed31f41 100644 --- a/SabreTools.DatTools/Splitter.cs +++ b/SabreTools.DatTools/Splitter.cs @@ -66,7 +66,7 @@ namespace SabreTools.DatTools // Now separate the roms accordingly Parallel.ForEach(datFile.Items.Keys, Globals.ParallelOptions, key => { - List items = datFile.Items[key]; + ConcurrentList items = datFile.Items[key]; foreach (DatItem item in items) { if (newExtA.Contains((item.GetName() ?? string.Empty).GetNormalizedExtension())) @@ -147,7 +147,7 @@ namespace SabreTools.DatTools // Now populate each of the DAT objects in turn Parallel.ForEach(datFile.Items.Keys, Globals.ParallelOptions, key => { - List items = datFile.Items[key]; + ConcurrentList items = datFile.Items[key]; foreach (DatItem item in items) { // If the file is not a Disk, Media, or Rom, continue @@ -248,7 +248,7 @@ namespace SabreTools.DatTools } // Clean the input list and set all games to be pathless - List items = datFile.Items[key]; + ConcurrentList items = datFile.Items[key]; items.ForEach(item => item.Machine.Name = Path.GetFileName(item.Machine.Name)); items.ForEach(item => item.Machine.Description = Path.GetFileName(item.Machine.Description)); @@ -336,7 +336,7 @@ namespace SabreTools.DatTools // Now populate each of the DAT objects in turn Parallel.ForEach(datFile.Items.Keys, Globals.ParallelOptions, key => { - List items = datFile.Items[key]; + ConcurrentList items = datFile.Items[key]; foreach (DatItem item in items) { // If the file is not a Rom, it automatically goes in the "lesser" dat @@ -504,7 +504,7 @@ namespace SabreTools.DatTools // Loop through and add the items for this index to the output Parallel.ForEach(datFile.Items.Keys, Globals.ParallelOptions, key => { - List items = DatItem.Merge(datFile.Items[key]); + ConcurrentList items = DatItem.Merge(datFile.Items[key]); // If the rom list is empty or null, just skip it if (items == null || items.Count == 0) diff --git a/SabreTools.DatTools/Verification.cs b/SabreTools.DatTools/Verification.cs index 8a9bf24b..53fd9d04 100644 --- a/SabreTools.DatTools/Verification.cs +++ b/SabreTools.DatTools/Verification.cs @@ -133,7 +133,7 @@ namespace SabreTools.DatTools var keys = datFile.Items.SortedKeys.ToList(); foreach (string key in keys) { - List items = datFile.Items[key]; + ConcurrentList items = datFile.Items[key]; for (int i = 0; i < items.Count; i++) { // Unmatched items will have a source ID of int.MaxValue, remove all others diff --git a/SabreTools.Filtering/Cleaner.cs b/SabreTools.Filtering/Cleaner.cs index 84ea0655..3a4b7c8f 100644 --- a/SabreTools.Filtering/Cleaner.cs +++ b/SabreTools.Filtering/Cleaner.cs @@ -163,7 +163,7 @@ namespace SabreTools.Filtering foreach (string key in keys) { // For every item in the current key - List items = datFile.Items[key]; + ConcurrentList items = datFile.Items[key]; foreach (DatItem item in items) { // If we have a null item, we can't clean it it @@ -250,7 +250,7 @@ namespace SabreTools.Filtering ConcurrentDictionary mapping = new ConcurrentDictionary(); Parallel.ForEach(datFile.Items.Keys, Globals.ParallelOptions, key => { - List items = datFile.Items[key]; + ConcurrentList items = datFile.Items[key]; foreach (DatItem item in items) { // If the key mapping doesn't exist, add it @@ -261,8 +261,8 @@ namespace SabreTools.Filtering // Now we loop through every item and update accordingly Parallel.ForEach(datFile.Items.Keys, Globals.ParallelOptions, key => { - List items = datFile.Items[key]; - List newItems = new List(); + ConcurrentList items = datFile.Items[key]; + ConcurrentList newItems = new ConcurrentList(); foreach (DatItem item in items) { // Update machine name @@ -543,7 +543,7 @@ namespace SabreTools.Filtering // For each rom, we want to update the game to be "/" Parallel.ForEach(datFile.Items.Keys, Globals.ParallelOptions, key => { - List items = datFile.Items[key]; + ConcurrentList items = datFile.Items[key]; for (int i = 0; i < items.Count; i++) { SetOneRomPerGame(items[i]); @@ -580,7 +580,7 @@ namespace SabreTools.Filtering // Now process all of the roms Parallel.ForEach(datFile.Items.Keys, Globals.ParallelOptions, key => { - List items = datFile.Items[key]; + ConcurrentList items = datFile.Items[key]; for (int j = 0; j < items.Count; j++) { DatItem item = items[j]; diff --git a/SabreTools.Filtering/ExtraIni.cs b/SabreTools.Filtering/ExtraIni.cs index 68418c1e..8b8c7f0b 100644 --- a/SabreTools.Filtering/ExtraIni.cs +++ b/SabreTools.Filtering/ExtraIni.cs @@ -118,7 +118,7 @@ namespace SabreTools.Filtering continue; // Get the list of DatItems for the machine - List datItems = datFile.Items[machine]; + ConcurrentList datItems = datFile.Items[machine]; // Try to get the map values, if possible combinedMachineMaps.TryGetValue(machine, out Dictionary machineMappings); diff --git a/SabreTools.Filtering/Filter.cs b/SabreTools.Filtering/Filter.cs index a5ff27e9..2fef477c 100644 --- a/SabreTools.Filtering/Filter.cs +++ b/SabreTools.Filtering/Filter.cs @@ -396,7 +396,7 @@ namespace SabreTools.Filtering { // For every item in the current key bool machinePass = true; - List items = datFile.Items[key]; + ConcurrentList items = datFile.Items[key]; foreach (DatItem item in items) { // If we have a null item, we can't pass it diff --git a/SabreTools.Filtering/Remover.cs b/SabreTools.Filtering/Remover.cs index d08c6f3d..8fc4e5c8 100644 --- a/SabreTools.Filtering/Remover.cs +++ b/SabreTools.Filtering/Remover.cs @@ -113,7 +113,7 @@ namespace SabreTools.Filtering { Parallel.ForEach(datFile.Items.Keys, Globals.ParallelOptions, key => { - List items = datFile.Items[key]; + ConcurrentList items = datFile.Items[key]; for (int j = 0; j < items.Count; j++) { DatItemRemover.RemoveFields(items[j]); diff --git a/SabreTools.Filtering/Splitter.cs b/SabreTools.Filtering/Splitter.cs index aec2573d..c7d9a1cd 100644 --- a/SabreTools.Filtering/Splitter.cs +++ b/SabreTools.Filtering/Splitter.cs @@ -227,7 +227,7 @@ namespace SabreTools.Filtering // If the parent exists and has items, we copy the items from the parent to the current game DatItem copyFrom = datFile.Items[game][0]; - List parentItems = datFile.Items[parent]; + ConcurrentList parentItems = datFile.Items[parent]; foreach (DatItem item in parentItems) { DatItem datItem = (DatItem)item.Clone(); @@ -288,7 +288,7 @@ namespace SabreTools.Filtering continue; // Add to the list of new device reference names - List devItems = datFile.Items[deviceReference]; + ConcurrentList devItems = datFile.Items[deviceReference]; newDeviceReferences.AddRange(devItems .Where(i => i.ItemType == ItemType.DeviceReference) .Select(i => (i as DeviceReference).Name)); @@ -331,7 +331,7 @@ namespace SabreTools.Filtering continue; // Add to the list of new slot option names - List slotItems = datFile.Items[slotOption]; + ConcurrentList slotItems = datFile.Items[slotOption]; newSlotOptions.AddRange(slotItems .Where(i => i.ItemType == ItemType.Slot) .Where(s => (s as Slot).SlotOptionsSpecified) @@ -396,7 +396,7 @@ namespace SabreTools.Filtering // If the parent exists and has items, we copy the items from the parent to the current game DatItem copyFrom = datFile.Items[game][0]; - List parentItems = datFile.Items[parent]; + ConcurrentList parentItems = datFile.Items[parent]; foreach (DatItem item in parentItems) { DatItem datItem = (DatItem)item.Clone(); @@ -409,7 +409,7 @@ namespace SabreTools.Filtering } // Now we want to get the parent romof tag and put it in each of the items - List items = datFile.Items[game]; + ConcurrentList items = datFile.Items[game]; string romof = datFile.Items[parent][0].Machine.RomOf; foreach (DatItem item in items) { @@ -454,7 +454,7 @@ namespace SabreTools.Filtering copyFrom = datFile.Items[parent][0]; } - List items = datFile.Items[game]; + ConcurrentList items = datFile.Items[game]; foreach (DatItem item in items) { // Special disk handling @@ -582,7 +582,7 @@ namespace SabreTools.Filtering continue; // If the parent exists and has items, we remove the items that are in the parent from the current game - List parentItems = datFile.Items[parent]; + ConcurrentList parentItems = datFile.Items[parent]; foreach (DatItem item in parentItems) { DatItem datItem = (DatItem)item.Clone(); @@ -621,7 +621,7 @@ namespace SabreTools.Filtering continue; // If the parent exists and has items, we remove the parent items from the current game - List parentItems = datFile.Items[parent]; + ConcurrentList parentItems = datFile.Items[parent]; foreach (DatItem item in parentItems) { DatItem datItem = (DatItem)item.Clone(); @@ -632,7 +632,7 @@ namespace SabreTools.Filtering } // Now we want to get the parent romof tag and put it in each of the remaining items - List items = datFile.Items[game]; + ConcurrentList items = datFile.Items[game]; string romof = datFile.Items[parent][0].Machine.RomOf; foreach (DatItem item in items) { @@ -650,7 +650,7 @@ namespace SabreTools.Filtering List games = datFile.Items.Keys.OrderBy(g => g).ToList(); foreach (string game in games) { - List items = datFile.Items[game]; + ConcurrentList items = datFile.Items[game]; foreach (DatItem item in items) { item.Machine.CloneOf = null; diff --git a/SabreTools.Test/DatFiles/ItemDictionaryTests.cs b/SabreTools.Test/DatFiles/ItemDictionaryTests.cs index 07416332..31b0a2a3 100644 --- a/SabreTools.Test/DatFiles/ItemDictionaryTests.cs +++ b/SabreTools.Test/DatFiles/ItemDictionaryTests.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; +using SabreTools.Core; using SabreTools.DatFiles; using SabreTools.DatItems; using SabreTools.DatItems.Formats; @@ -19,7 +20,7 @@ namespace SabreTools.Test.DatFiles // Setup the dictionary var dict = new ItemDictionary { - ["game-1"] = new List + ["game-1"] = new ConcurrentList { new Rom { @@ -38,7 +39,7 @@ namespace SabreTools.Test.DatFiles Machine = new Machine { Name = "game-1" }, }, }, - ["game-2"] = new List + ["game-2"] = new ConcurrentList { new Rom { @@ -69,8 +70,8 @@ namespace SabreTools.Test.DatFiles // Setup the dictionary var dict = new ItemDictionary { - ["game-1"] = new List { new Rom(), }, - ["game-2"] = new List(), + ["game-1"] = new ConcurrentList { new Rom(), }, + ["game-2"] = new ConcurrentList(), ["game-3"] = null, }; @@ -84,7 +85,7 @@ namespace SabreTools.Test.DatFiles // Setup the dictionary var dict = new ItemDictionary { - ["game-1"] = new List + ["game-1"] = new ConcurrentList { new Rom { @@ -119,7 +120,7 @@ namespace SabreTools.Test.DatFiles // Setup the dictionary var dict = new ItemDictionary { - ["game-1"] = new List + ["game-1"] = new ConcurrentList { new Rom { @@ -158,7 +159,7 @@ namespace SabreTools.Test.DatFiles // Setup the dictionary var dict = new ItemDictionary { - ["game-1"] = new List + ["game-1"] = new ConcurrentList { new Rom {