// // 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 bytes in a stream. /// /// This is normally used to represent regions of a SparseStream that /// are actually stored in the underlying storage medium (rather than implied /// zero bytes). Extents are stored as a zero-based byte offset (from the /// beginning of the stream), and a byte length. public sealed class StreamExtent : IEquatable, IComparable { /// /// Initializes a new instance of the StreamExtent class. /// /// The start of the extent. /// The length of the extent. public StreamExtent(long start, long length) { Start = start; Length = length; } /// /// Gets the start of the extent (in bytes). /// public long Length { get; } /// /// Gets the start of the extent (in bytes). /// public long Start { get; } /// /// Compares this stream extent to another. /// /// The extent to compare. /// Value greater than zero if this extent starts after /// other, zero if they start at the same position, else /// a value less than zero. public int CompareTo(StreamExtent other) { if (Start > other.Start) { return 1; } if (Start == other.Start) { return 0; } return -1; } /// /// Indicates if this StreamExtent is equal to another. /// /// The extent to compare. /// true if the extents are equal, else false. public bool Equals(StreamExtent other) { if (other == null) { return false; } return Start == other.Start && Length == other.Length; } /// /// Calculates the union of a list of extents with another extent. /// /// The list of extents. /// The other extent. /// The union of the extents. public static IEnumerable Union(IEnumerable extents, StreamExtent other) { List otherList = new List(); otherList.Add(other); return Union(extents, otherList); } /// /// Calculates the union of the extents of multiple streams. /// /// The stream extents. /// The union of the extents from multiple streams. /// A typical use of this method is to calculate the combined set of /// stored extents from a number of overlayed sparse streams. public static IEnumerable Union(params IEnumerable[] streams) { long extentStart = long.MaxValue; long extentEnd = 0; // Initialize enumerations and find first stored byte position IEnumerator[] enums = new IEnumerator[streams.Length]; bool[] streamsValid = new bool[streams.Length]; int validStreamsRemaining = 0; for (int i = 0; i < streams.Length; ++i) { enums[i] = streams[i].GetEnumerator(); streamsValid[i] = enums[i].MoveNext(); if (streamsValid[i]) { ++validStreamsRemaining; if (enums[i].Current.Start < extentStart) { extentStart = enums[i].Current.Start; extentEnd = enums[i].Current.Start + enums[i].Current.Length; } } } while (validStreamsRemaining > 0) { // Find the end of this extent bool foundIntersection; do { foundIntersection = false; validStreamsRemaining = 0; for (int i = 0; i < streams.Length; ++i) { while (streamsValid[i] && enums[i].Current.Start + enums[i].Current.Length <= extentEnd) { streamsValid[i] = enums[i].MoveNext(); } if (streamsValid[i]) { ++validStreamsRemaining; } if (streamsValid[i] && enums[i].Current.Start <= extentEnd) { extentEnd = enums[i].Current.Start + enums[i].Current.Length; foundIntersection = true; streamsValid[i] = enums[i].MoveNext(); } } } while (foundIntersection && validStreamsRemaining > 0); // Return the discovered extent yield return new StreamExtent(extentStart, extentEnd - extentStart); // Find the next extent start point extentStart = long.MaxValue; validStreamsRemaining = 0; for (int i = 0; i < streams.Length; ++i) { if (streamsValid[i]) { ++validStreamsRemaining; if (enums[i].Current.Start < extentStart) { extentStart = enums[i].Current.Start; extentEnd = enums[i].Current.Start + enums[i].Current.Length; } } } } } /// /// Calculates the intersection of the extents of a stream with another extent. /// /// The stream extents. /// The extent to intersect. /// The intersection of the extents. public static IEnumerable Intersect(IEnumerable extents, StreamExtent other) { List otherList = new List(1); otherList.Add(other); return Intersect(extents, otherList); } /// /// Calculates the intersection of the extents of multiple streams. /// /// The stream extents. /// The intersection of the extents from multiple streams. /// A typical use of this method is to calculate the extents in a /// region of a stream.. public static IEnumerable Intersect(params IEnumerable[] streams) { long extentStart = long.MinValue; long extentEnd = long.MaxValue; IEnumerator[] enums = new IEnumerator[streams.Length]; for (int i = 0; i < streams.Length; ++i) { enums[i] = streams[i].GetEnumerator(); if (!enums[i].MoveNext()) { // Gone past end of one stream (in practice was empty), so no intersections yield break; } } int overlapsFound = 0; while (true) { // We keep cycling round the streams, until we get streams.Length continuous overlaps for (int i = 0; i < streams.Length; ++i) { // Move stream on past all extents that are earlier than our candidate start point while (enums[i].Current.Length == 0 || enums[i].Current.Start + enums[i].Current.Length <= extentStart) { if (!enums[i].MoveNext()) { // Gone past end of this stream, no more intersections possible yield break; } } // If this stream has an extent that spans over the candidate start point if (enums[i].Current.Start <= extentStart) { extentEnd = Math.Min(extentEnd, enums[i].Current.Start + enums[i].Current.Length); overlapsFound++; } else { extentStart = enums[i].Current.Start; extentEnd = extentStart + enums[i].Current.Length; overlapsFound = 1; } // We've just done a complete loop of all streams, they overlapped this start position // and we've cut the extent's end down to the shortest run. if (overlapsFound == streams.Length) { yield return new StreamExtent(extentStart, extentEnd - extentStart); extentStart = extentEnd; extentEnd = long.MaxValue; overlapsFound = 0; } } } } /// /// Calculates the subtraction of the extents of a stream by another extent. /// /// The stream extents. /// The extent to subtract. /// The subtraction of other from extents. public static IEnumerable Subtract(IEnumerable extents, StreamExtent other) { return Subtract(extents, new[] { other }); } /// /// Calculates the subtraction of the extents of a stream by another stream. /// /// The stream extents to subtract from. /// The stream extents to subtract. /// The subtraction of the extents of b from a. public static IEnumerable Subtract(IEnumerable a, IEnumerable b) { return Intersect(a, Invert(b)); } /// /// Calculates the inverse of the extents of a stream. /// /// The stream extents to inverse. /// The inverted extents. /// /// This method assumes a logical stream addressable from 0 to long.MaxValue, and is undefined /// should any stream extent start at less than 0. To constrain the extents to a specific range, use the /// Intersect method. /// public static IEnumerable Invert(IEnumerable extents) { StreamExtent last = new StreamExtent(0, 0); foreach (StreamExtent extent in extents) { // Skip over any 'noise' if (extent.Length == 0) { continue; } long lastEnd = last.Start + last.Length; if (lastEnd < extent.Start) { yield return new StreamExtent(lastEnd, extent.Start - lastEnd); } last = extent; } long finalEnd = last.Start + last.Length; if (finalEnd < long.MaxValue) { yield return new StreamExtent(finalEnd, long.MaxValue - finalEnd); } } /// /// Offsets the extents of a stream. /// /// The stream extents. /// The amount to offset the extents by. /// The stream extents, offset by delta. public static IEnumerable Offset(IEnumerable stream, long delta) { foreach (StreamExtent extent in stream) { yield return new StreamExtent(extent.Start + delta, extent.Length); } } /// /// Returns the number of blocks containing stream data. /// /// The stream extents. /// The size of each block. /// The number of blocks containing stream data. /// This method logically divides the stream into blocks of a specified /// size, then indicates how many of those blocks contain actual stream data. public static long BlockCount(IEnumerable stream, long blockSize) { long totalBlocks = 0; long lastBlock = -1; foreach (StreamExtent extent in stream) { if (extent.Length > 0) { long extentStartBlock = extent.Start / blockSize; long extentNextBlock = MathUtilities.Ceil(extent.Start + extent.Length, blockSize); long extentNumBlocks = extentNextBlock - extentStartBlock; if (extentStartBlock == lastBlock) { extentNumBlocks--; } lastBlock = extentNextBlock - 1; totalBlocks += extentNumBlocks; } } return totalBlocks; } /// /// Returns all of the blocks containing stream data. /// /// The stream extents. /// The size of each block. /// Ranges of blocks, as block indexes. /// This method logically divides the stream into blocks of a specified /// size, then indicates ranges of blocks that contain stream data. public static IEnumerable> Blocks(IEnumerable stream, long blockSize) { long? rangeStart = null; long rangeLength = 0; foreach (StreamExtent extent in stream) { if (extent.Length > 0) { long extentStartBlock = extent.Start / blockSize; long extentNextBlock = MathUtilities.Ceil(extent.Start + extent.Length, blockSize); if (rangeStart != null && extentStartBlock > rangeStart + rangeLength) { // This extent is non-contiguous (in terms of blocks), so write out the last range and start new yield return new Range((long)rangeStart, rangeLength); rangeStart = extentStartBlock; } else if (rangeStart == null) { // First extent, so start first range rangeStart = extentStartBlock; } // Set the length of the current range, based on the end of this extent rangeLength = extentNextBlock - (long)rangeStart; } } // Final range (if any ranges at all) hasn't been returned yet, so do that now if (rangeStart != null) { yield return new Range((long)rangeStart, rangeLength); } } /// /// The equality operator. /// /// The first extent to compare. /// The second extent to compare. /// Whether the two extents are equal. public static bool operator ==(StreamExtent a, StreamExtent b) { if (ReferenceEquals(a, null)) { return ReferenceEquals(b, null); } return a.Equals(b); } /// /// The inequality operator. /// /// The first extent to compare. /// The second extent to compare. /// Whether the two extents are different. public static bool operator !=(StreamExtent a, StreamExtent b) { return !(a == b); } /// /// The less-than operator. /// /// The first extent to compare. /// The second extent to compare. /// Whether a is less than b. public static bool operator <(StreamExtent a, StreamExtent b) { return a.CompareTo(b) < 0; } /// /// The greater-than operator. /// /// The first extent to compare. /// The second extent to compare. /// Whether a is greater than b. public static bool operator >(StreamExtent a, StreamExtent b) { return a.CompareTo(b) > 0; } /// /// Returns a string representation of the extent as [start:+length]. /// /// The string representation. public override string ToString() { return "[" + Start + ":+" + Length + "]"; } /// /// Indicates if this stream extent is equal to another object. /// /// The object to test. /// true if obj is equivalent, else false. public override bool Equals(object obj) { return Equals(obj as StreamExtent); } /// /// Gets a hash code for this extent. /// /// The extent's hash code. public override int GetHashCode() { return Start.GetHashCode() ^ Length.GetHashCode(); } } }