// /*************************************************************************** // Aaru Data Preservation Suite // ---------------------------------------------------------------------------- // // Filename : ExtentsUInt.cs // Author(s) : Natalia Portillo // // Component : Extent helpers. // // --[ Description ] ---------------------------------------------------------- // // Provides extents for uint types. // // --[ License ] -------------------------------------------------------------- // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be included // in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // // ---------------------------------------------------------------------------- // Copyright © 2011-2023 Natalia Portillo // ****************************************************************************/ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; namespace Aaru.CommonTypes.Extents; /// Implements extents for [SuppressMessage("ReSharper", "UnusedMember.Global")] [SuppressMessage("ReSharper", "UnusedType.Global")] [SuppressMessage("ReSharper", "MemberCanBePrivate.Global")] public sealed class ExtentsUInt { List> _backend; /// Initialize an empty list of extents public ExtentsUInt() => _backend = new List>(); /// Initializes extents with an specific list /// List of extents as tuples "start, end" public ExtentsUInt(IEnumerable> list) { _backend = new List>(); // This ensure no overlapping extents are added on creation foreach(Tuple t in list) Add(t.Item1, t.Item2); } /// Gets a count of how many extents are stored public int Count => _backend.Count; /// Adds the specified number to the corresponding extent, or creates a new one /// public void Add(uint item) { Tuple removeOne = null; Tuple removeTwo = null; Tuple itemToAdd = null; for(var i = 0; i < _backend.Count; i++) { // Already contained in an extent if(item >= _backend[i].Item1 && item <= _backend[i].Item2) return; // Expands existing extent start if(item == _backend[i].Item1 - 1) { removeOne = _backend[i]; if(i > 0 && item == _backend[i - 1].Item2 + 1) { removeTwo = _backend[i - 1]; itemToAdd = new Tuple(_backend[i - 1].Item1, _backend[i].Item2); } else itemToAdd = new Tuple(item, _backend[i].Item2); break; } // Expands existing extent end if(item != _backend[i].Item2 + 1) continue; removeOne = _backend[i]; if(i < _backend.Count - 1 && item == _backend[i + 1].Item1 - 1) { removeTwo = _backend[i + 1]; itemToAdd = new Tuple(_backend[i].Item1, _backend[i + 1].Item2); } else itemToAdd = new Tuple(_backend[i].Item1, item); break; } if(itemToAdd != null) { _backend.Remove(removeOne); _backend.Remove(removeTwo); _backend.Add(itemToAdd); } else _backend.Add(new Tuple(item, item)); // Sort _backend = _backend.OrderBy(t => t.Item1).ToList(); } /// Adds a new extent /// First element of the extent /// /// Last element of the extent or if is true how many elements the extent runs /// for /// /// If set to true, indicates how many elements the extent runs for public void Add(uint start, uint end, bool run = false) { uint realEnd; if(run) realEnd = start + end - 1; else realEnd = end; // TODO: Optimize this for(uint t = start; t <= realEnd; t++) Add(t); } /// Checks if the specified item is contained by an extent on this instance /// Item to search for /// true if any of the extents on this instance contains the item public bool Contains(uint item) => _backend.Any(extent => item >= extent.Item1 && item <= extent.Item2); /// Removes all extents from this instance public void Clear() => _backend.Clear(); /// Removes an item from the extents in this instance /// Item to remove /// true if the item was contained in a known extent and removed, false otherwise public bool Remove(uint item) { Tuple toRemove = null; Tuple toAddOne = null; Tuple toAddTwo = null; foreach(Tuple extent in _backend) { // Extent is contained and not a border if(item > extent.Item1 && item < extent.Item2) { toRemove = extent; toAddOne = new Tuple(extent.Item1, item - 1); toAddTwo = new Tuple(item + 1, extent.Item2); break; } // Extent is left border, but not only element if(item == extent.Item1 && item != extent.Item2) { toRemove = extent; toAddOne = new Tuple(item + 1, extent.Item2); break; } // Extent is right border, but not only element if(item != extent.Item1 && item == extent.Item2) { toRemove = extent; toAddOne = new Tuple(extent.Item1, item - 1); break; } // Extent is only element if(item != extent.Item1 || item != extent.Item2) continue; toRemove = extent; break; } // Item not found if(toRemove == null) return false; _backend.Remove(toRemove); if(toAddOne != null) _backend.Add(toAddOne); if(toAddTwo != null) _backend.Add(toAddTwo); // Sort _backend = _backend.OrderBy(t => t.Item1).ToList(); return true; } /// /// Converts the list of extents to an array of where T1 is first element of the extent and /// T2 is last element /// /// Array of public Tuple[] ToArray() => _backend.ToArray(); /// Gets the first element of the extent that contains the specified item /// Item /// First element of extent /// true if item was found in an extent, false otherwise public bool GetStart(uint item, out uint start) { start = 0; foreach(Tuple extent in _backend.Where(extent => item >= extent.Item1 && item <= extent.Item2)) { start = extent.Item1; return true; } return false; } }