1 // 2 // Copyright (c) 2010-2024 Antmicro 3 // Copyright (c) 2011-2015 Realtime Embedded 4 // 5 // This file is licensed under the MIT License. 6 // Full license text is available in 'licenses/MIT.txt'. 7 // 8 using System; 9 using System.Collections; 10 using System.Collections.Generic; 11 using System.Linq; 12 using Antmicro.Renode.Core; 13 using Antmicro.Renode.Exceptions; 14 15 namespace Antmicro.Renode.Core 16 { 17 public struct Range 18 { RangeAntmicro.Renode.Core.Range19 public Range(ulong startAddress, ulong size) 20 { 21 StartAddress = startAddress; 22 EndAddress = startAddress + size - 1; 23 24 // Check if overflow occurred. 25 CreationAssert(EndAddress >= StartAddress, startAddress, () => $"size is too large: 0x{size:X}"); 26 } 27 28 /// <summary> 29 /// Checks if this <c>Range</c> can be expanded by <c>range</c> so that 30 /// <c>this.Contains(address) || range.Contains(address)</c> equals 31 /// <c>this.Expand(range).Contains(address)</c> for any address. 32 /// </summary> CanBeExpandedByAntmicro.Renode.Core.Range33 public bool CanBeExpandedBy(Range range) 34 { 35 return Intersects(range) || IsAdjacentTo(range); 36 } 37 ContainsAntmicro.Renode.Core.Range38 public bool Contains(ulong address) 39 { 40 return address >= StartAddress && address <= EndAddress; 41 } 42 ContainsAntmicro.Renode.Core.Range43 public bool Contains(long address) 44 { 45 // Range operates on positive numbers 46 // so it cannot contain a negative number. 47 if(address < 0) 48 { 49 return false; 50 } 51 52 return Contains((ulong)address); 53 } 54 ContainsAntmicro.Renode.Core.Range55 public bool Contains(Range range) 56 { 57 return range.StartAddress >= StartAddress && range.EndAddress <= EndAddress; 58 } 59 60 /// <param name="range"> 61 /// <c>range</c> has to overlap or be adjacent to this <c>Range</c> 62 /// which can be tested with <c>CanBeExpandedBy(range)</c>. 63 /// An <c>ArgumentException</c> is thrown if the condition isn't satisfied. 64 /// </param> ExpandAntmicro.Renode.Core.Range65 public Range Expand(Range range) 66 { 67 if(!CanBeExpandedBy(range)) 68 { 69 throw new ArgumentException($"{this} can't be expanded by {range}."); 70 } 71 var startAddress = Math.Min(StartAddress, range.StartAddress); 72 var endAddress = Math.Max(EndAddress, range.EndAddress); 73 return startAddress.To(endAddress); 74 } 75 76 /// <returns>Intersection if ranges overlap, <c>null</c> otherwise.</returns> IntersectAntmicro.Renode.Core.Range77 public Range? Intersect(Range range) 78 { 79 var startAddress = Math.Max(StartAddress, range.StartAddress); 80 var endAddress = Math.Min(EndAddress, range.EndAddress); 81 if(startAddress > endAddress) 82 { 83 return null; 84 } 85 return new Range(startAddress, endAddress - startAddress + 1); 86 } 87 IsAdjacentToAntmicro.Renode.Core.Range88 public bool IsAdjacentTo(Range range) 89 { 90 return (StartAddress != 0x0 && StartAddress == range.EndAddress + 1) 91 || (EndAddress != ulong.MaxValue && EndAddress == range.StartAddress - 1); 92 } 93 SubtractAntmicro.Renode.Core.Range94 public List<Range> Subtract(Range sub) 95 { 96 // If the subtracted range does not intersect this range, return this range 97 if(!sub.Intersects(this)) 98 { 99 return new List<Range> { this }; 100 } 101 102 // If the subtracted range contains this range, return an empty list 103 if(sub.Contains(this)) 104 { 105 return new List<Range> { }; 106 } 107 108 // If the subtracted range contains the start of this range, 109 // return a range from the end of the subtracted range to the end of this range 110 if(sub.Contains(StartAddress)) 111 { 112 return new List<Range> { new Range(sub.EndAddress + 1, EndAddress - sub.EndAddress) }; 113 } 114 115 // If the subtracted range contains the end of this range, 116 // return a range from the start of this range to the start of the subtracted range 117 if(sub.Contains(EndAddress)) 118 { 119 return new List<Range> { new Range(StartAddress, sub.StartAddress - StartAddress) }; 120 } 121 122 // If the subtracted range is contained within this range, return two ranges: 123 // one from the start of this range to the start of the subtracted range, and 124 // one from the end of the subtracted range to the end of this range. 125 // We probably don't need to check this because it's the only possibility left 126 if(this.Contains(sub)) 127 { 128 return new List<Range> 129 { 130 new Range(StartAddress, sub.StartAddress - StartAddress), 131 new Range(sub.EndAddress + 1, EndAddress - sub.EndAddress) 132 }; 133 } 134 135 throw new Exception("Unreachable"); 136 } 137 IntersectsAntmicro.Renode.Core.Range138 public bool Intersects(Range range) 139 { 140 return Intersect(range).HasValue; 141 } 142 143 public ulong StartAddress 144 { 145 get; 146 private set; 147 } 148 149 public ulong EndAddress 150 { 151 get; 152 private set; 153 } 154 155 public ulong Size 156 { 157 get 158 { 159 return EndAddress - StartAddress + 1; 160 } 161 } 162 ShiftByAntmicro.Renode.Core.Range163 public Range ShiftBy(long shiftValue) 164 { 165 return new Range(checked(shiftValue >= 0 ? StartAddress + (ulong)shiftValue : StartAddress - (ulong)(-shiftValue)), Size); 166 } 167 MoveToZeroAntmicro.Renode.Core.Range168 public Range MoveToZero() 169 { 170 return new Range(0, Size); 171 } 172 ToStringAntmicro.Renode.Core.Range173 public override string ToString() 174 { 175 return string.Format("<0x{0:X8}, 0x{1:X8}>", StartAddress, EndAddress); 176 } 177 EqualsAntmicro.Renode.Core.Range178 public override bool Equals(object obj) 179 { 180 if(obj == null) 181 { 182 return false; 183 } 184 if(obj.GetType() != typeof(Range)) 185 { 186 return false; 187 } 188 var other = (Range)obj; 189 return this == other; 190 } 191 GetHashCodeAntmicro.Renode.Core.Range192 public override int GetHashCode() 193 { 194 unchecked 195 { 196 return 7 * StartAddress.GetHashCode() ^ 31 * EndAddress.GetHashCode(); 197 } 198 } 199 operator ==Antmicro.Renode.Core.Range200 public static bool operator==(Range range, Range other) 201 { 202 return range.StartAddress == other.StartAddress && range.EndAddress == other.EndAddress; 203 } 204 operator !=Antmicro.Renode.Core.Range205 public static bool operator!=(Range range, Range other) 206 { 207 return !(range == other); 208 } 209 operator +Antmicro.Renode.Core.Range210 public static Range operator+(Range range, long addend) 211 { 212 return range.ShiftBy(addend); 213 } 214 operator -Antmicro.Renode.Core.Range215 public static Range operator-(Range range, long minuend) 216 { 217 return range.ShiftBy(-minuend); 218 } 219 CreationAssertAntmicro.Renode.Core.Range220 internal static void CreationAssert(bool condition, ulong startAddress, Func<string> reason) 221 { 222 if(!condition) 223 { 224 throw new RecoverableException($"Could not create range at 0x{startAddress:X}; {reason()}"); 225 } 226 } 227 } 228 229 public interface IReadOnlyMinimalRangesCollection : IEnumerable<Range> 230 { ContainsOverlappingRange(Range range)231 bool ContainsOverlappingRange(Range range); ContainsWholeRange(Range range)232 bool ContainsWholeRange(Range range); ContainsPoint(ulong point)233 bool ContainsPoint(ulong point); 234 } 235 236 public class MinimalRangesCollection : IReadOnlyMinimalRangesCollection, ICoalescable<MinimalRangesCollection> 237 { MinimalRangesCollection(IEnumerable<Range> rangeEnumerable = null)238 public MinimalRangesCollection(IEnumerable<Range> rangeEnumerable = null) 239 { 240 if(rangeEnumerable != null) 241 { 242 AddAll(rangeEnumerable); 243 } 244 } 245 246 /// <summary> 247 /// Adds a <c>range</c> expanding any elements, if possible. Therefore ranges overlapping, 248 /// and adjacent to the <c>range</c> are removed from the collection and re-added as a 249 /// single expanded <c>Range</c>. 250 /// </summary> Add(Range range)251 public void Add(Range range) 252 { 253 // ToArray is necessary because ranges gets modified in the code block. 254 foreach(var expandableRange in ranges.Where(collectionRange => collectionRange.CanBeExpandedBy(range)).ToArray()) 255 { 256 ranges.Remove(expandableRange); 257 range = range.Expand(expandableRange); 258 } 259 ranges.Add(range); 260 } 261 AddAll(IEnumerable<Range> source)262 public void AddAll(IEnumerable<Range> source) 263 { 264 foreach(var range in source) 265 { 266 Add(range); 267 } 268 } 269 Coalesce(MinimalRangesCollection source)270 public void Coalesce(MinimalRangesCollection source) 271 { 272 AddAll(source.AsEnumerable()); 273 } 274 Clear()275 public void Clear() 276 { 277 ranges.Clear(); 278 } 279 ContainsOverlappingRange(Range range)280 public bool ContainsOverlappingRange(Range range) 281 { 282 return ranges.Any(existingRange => existingRange.Intersects(range)); 283 } 284 ContainsWholeRange(Range range)285 public bool ContainsWholeRange(Range range) 286 { 287 // Ranges are merged when added to the collection which means it's only possible if one of elements contains this whole range. 288 return ranges.Any(existingRange => existingRange.Contains(range)); 289 } 290 ContainsPoint(ulong point)291 public bool ContainsPoint(ulong point) 292 { 293 return ranges.Any(existingRange => existingRange.Contains(point)); 294 } 295 GetEnumerator()296 public IEnumerator GetEnumerator() 297 { 298 return ranges.GetEnumerator(); 299 } 300 Remove(Range range)301 public bool Remove(Range range) 302 { 303 return ranges.SubtractAll(range); 304 } 305 GetEnumerator()306 IEnumerator<Range> IEnumerable<Range>.GetEnumerator() 307 { 308 return ((IEnumerable<Range>)ranges).GetEnumerator(); 309 } 310 311 public int Count => ranges.Count; 312 313 private readonly HashSet<Range> ranges = new HashSet<Range>(); 314 } 315 316 public static class RangeExtensions 317 { 318 /// <summary> 319 /// Subtracts the given <c>range</c> from each overlapping element in <c>ranges</c>. 320 /// </summary> 321 /// <returns>True if <c>range</c> was subtracted from any element in <c>ranges</c>.</returns> SubtractAll(this ICollection<Range> ranges, Range range)322 public static bool SubtractAll(this ICollection<Range> ranges, Range range) 323 { 324 // ToArray is necessary because ranges gets modified in the code block. 325 var overlappingRanges = ranges.Where(collectionRange => collectionRange.Intersects(range)).ToArray(); 326 foreach(var overlappingRange in overlappingRanges) 327 { 328 ranges.Remove(overlappingRange); 329 foreach(var remainingRange in overlappingRange.Subtract(range)) 330 { 331 ranges.Add(remainingRange); 332 } 333 } 334 return overlappingRanges.Any(); 335 } 336 By(this ulong startAddress, ulong size)337 public static Range By(this ulong startAddress, ulong size) 338 { 339 return new Range(startAddress, size); 340 } 341 By(this long startAddress, ulong size)342 public static Range By(this long startAddress, ulong size) 343 { 344 return new Range(checked((ulong)startAddress), size); 345 } 346 By(this long startAddress, long size)347 public static Range By(this long startAddress, long size) 348 { 349 return new Range(checked((ulong)startAddress), checked((ulong)size)); 350 } 351 By(this int startAddress, ulong size)352 public static Range By(this int startAddress, ulong size) 353 { 354 return new Range(checked((ulong)startAddress), size); 355 } 356 By(this int startAddress, long size)357 public static Range By(this int startAddress, long size) 358 { 359 return new Range(checked((ulong)startAddress), checked((ulong)size)); 360 } 361 By(this uint startAddress, ulong size)362 public static Range By(this uint startAddress, ulong size) 363 { 364 return new Range(startAddress, size); 365 } 366 By(this uint startAddress, long size)367 public static Range By(this uint startAddress, long size) 368 { 369 return new Range(startAddress, checked((ulong)size)); 370 } 371 372 /// <remarks>The returned `Range` contains <c>endAddress</c>.</remarks> To(this int startAddress, ulong endAddress)373 public static Range To(this int startAddress, ulong endAddress) 374 { 375 return checked((ulong)startAddress).To(endAddress); 376 } 377 378 /// <remarks>The returned `Range` contains <c>endAddress</c>.</remarks> To(this long startAddress, ulong endAddress)379 public static Range To(this long startAddress, ulong endAddress) 380 { 381 return checked((ulong)startAddress).To(endAddress); 382 } 383 384 /// <remarks>The returned `Range` contains <c>endAddress</c>.</remarks> To(this ulong startAddress, ulong endAddress)385 public static Range To(this ulong startAddress, ulong endAddress) 386 { 387 // Moving these to a `Range` constructor would be much better but we can't have 388 // a second constructor with two `ulong` arguments. 389 Range.CreationAssert( 390 startAddress != 0 || endAddress != ulong.MaxValue, 391 startAddress, () => "<0, 2^64-1> ranges aren't currently supported" 392 ); 393 394 // The constructor would also throw but the message would be that size is too large 395 // if `startAddress` is higher than `endAddress` which can be misleading. 396 Range.CreationAssert( 397 startAddress <= endAddress, 398 startAddress, () => "the start address can't be higher than the end address" 399 ); 400 return new Range(startAddress, checked(endAddress - startAddress + 1)); 401 } 402 } 403 } 404 405