//
// 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 + "]";
}
}
}