// // Copyright (c) 2008-2011, Kenneth Bell // // 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. // using System; using System.Collections.Generic; namespace DiscUtils.Streams { /// /// Represents a range of values. /// /// The type of the offset element. /// The type of the size element. public class Range : IEquatable> where TOffset : IEquatable where TCount : IEquatable { /// /// Initializes a new instance of the Range class. /// /// The offset (i.e. start) of the range. /// The size of the range. public Range(TOffset offset, TCount count) { Offset = offset; Count = count; } /// /// Gets the size of the range. /// public TCount Count { get; } /// /// Gets the offset (i.e. start) of the range. /// public TOffset Offset { get; } #region IEquatable> Members /// /// Compares this range to another. /// /// The range to compare. /// true if the ranges are equivalent, else false. public bool Equals(Range other) { if (other == null) { return false; } return Offset.Equals(other.Offset) && Count.Equals(other.Count); } #endregion /// /// Merges sets of ranges into chunks. /// /// The ranges to merge. /// The size of each chunk. /// Ranges combined into larger chunks. /// The type of the offset and count in the ranges. public static IEnumerable> Chunked(IEnumerable> ranges, T chunkSize) where T : struct, IEquatable, IComparable { T? chunkStart = Numbers.Zero; T chunkLength = Numbers.Zero; foreach (Range range in ranges) { if (Numbers.NotEqual(range.Count, Numbers.Zero)) { T rangeStart = Numbers.RoundDown(range.Offset, chunkSize); T rangeNext = Numbers.RoundUp(Numbers.Add(range.Offset, range.Count), chunkSize); if (chunkStart.HasValue && Numbers.GreaterThan(rangeStart, Numbers.Add(chunkStart.Value, chunkLength))) { // This extent is non-contiguous (in terms of blocks), so write out the last range and start new yield return new Range(chunkStart.Value, chunkLength); chunkStart = rangeStart; } else if (!chunkStart.HasValue) { // First extent, so start first range chunkStart = rangeStart; } // Set the length of the current range, based on the end of this extent chunkLength = Numbers.Subtract(rangeNext, chunkStart.Value); } } // Final range (if any ranges at all) hasn't been returned yet, so do that now if (chunkStart.HasValue) { yield return new Range(chunkStart.Value, chunkLength); } } /// /// Returns a string representation of the extent as [start:+length]. /// /// The string representation. public override string ToString() { return "[" + Offset + ":+" + Count + "]"; } } }