1- // Licensed to the .NET Foundation under one or more agreements.
1+ // Licensed to the .NET Foundation under one or more agreements.
22// The .NET Foundation licenses this file to you under the MIT license.
33// See the LICENSE file in the project root for more information.
44
@@ -299,7 +299,230 @@ public static void CopyTo<T>(this T[] array, Span<T> destination)
299299 [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
300300 public static void CopyTo < T > ( this T [ ] array , Memory < T > destination )
301301 {
302- array . CopyTo ( destination . Span ) ;
302+ array . CopyTo ( destination . Span ) ;
303+ }
304+
305+ //
306+ // Overlaps
307+ // ========
308+ //
309+ // The following methods can be used to determine if two sequences
310+ // overlap in memory.
311+ //
312+ // Two sequences overlap if they have positions in common and neither
313+ // is empty. Empty sequences do not overlap with any other sequence.
314+ //
315+ // If two sequences overlap, the element offset is the number of
316+ // elements by which the second sequence is offset from the first
317+ // sequence (i.e., second minus first). An exception is thrown if the
318+ // number is not a whole number, which can happen when a sequence of a
319+ // smaller type is cast to a sequence of a larger type with unsafe code
320+ // or NonPortableCast. If the sequences do not overlap, the offset is
321+ // meaningless and arbitrarily set to zero.
322+ //
323+ // Implementation
324+ // --------------
325+ //
326+ // Implementing this correctly is quite tricky due of two problems:
327+ //
328+ // * If the sequences refer to two different objects on the managed
329+ // heap, the garbage collector can move them freely around or change
330+ // their relative order in memory.
331+ //
332+ // * The distance between two sequences can be greater than
333+ // int.MaxValue (on a 32-bit system) or long.MaxValue (on a 64-bit
334+ // system).
335+ //
336+ // (For simplicity, the following text assumes a 32-bit system, but
337+ // everything also applies to a 64-bit system if every 32 is replaced a
338+ // 64.)
339+ //
340+ // The first problem is solved by calculating the distance with exactly
341+ // one atomic operation. If the garbage collector happens to move the
342+ // sequences afterwards and the sequences overlapped before, they will
343+ // still overlap after the move and their distance hasn't changed. If
344+ // the sequences did not overlap, the distance can change but the
345+ // sequences still won't overlap.
346+ //
347+ // The second problem is solved by making all addresses relative to the
348+ // start of the first sequence and performing all operations in
349+ // unsigned integer arithmetic modulo 2³².
350+ //
351+ // Example
352+ // -------
353+ //
354+ // Let's say there are two sequences, x and y. Let
355+ //
356+ // ref T xRef = x.DangerousGetPinnableReference()
357+ // uint xLength = x.Length * Unsafe.SizeOf<T>()
358+ // ref T yRef = y.DangerousGetPinnableReference()
359+ // uint yLength = y.Length * Unsafe.SizeOf<T>()
360+ //
361+ // Visually, the two sequences are located somewhere in the 32-bit
362+ // address space as follows:
363+ //
364+ // [----------------------------------------------) normal address space
365+ // 0 2³²
366+ // [------------------) first sequence
367+ // xRef xRef + xLength
368+ // [--------------------------) . second sequence
369+ // yRef . yRef + yLength
370+ // : . . .
371+ // : . . .
372+ // . . .
373+ // . . .
374+ // . . .
375+ // [----------------------------------------------) relative address space
376+ // 0 . . 2³²
377+ // [------------------) : first sequence
378+ // x1 . x2 :
379+ // -------------) [------------- second sequence
380+ // y2 y1
381+ //
382+ // The idea is to make all addresses relative to xRef: Let x1 be the
383+ // start address of x in this relative address space, x2 the end
384+ // address of x, y1 the start address of y, and y2 the end address of
385+ // y:
386+ //
387+ // nuint x1 = 0
388+ // nuint x2 = xLength
389+ // nuint y1 = (nuint)Unsafe.ByteOffset(xRef, yRef)
390+ // nuint y2 = y1 + yLength
391+ //
392+ // xRef relative to xRef is 0.
393+ //
394+ // x2 is simply x1 + xLength. This cannot overflow.
395+ //
396+ // yRef relative to xRef is (yRef - xRef). If (yRef - xRef) is
397+ // negative, casting it to an unsigned 32-bit integer turns it into
398+ // (yRef - xRef + 2³²). So, in the example above, y1 moves to the right
399+ // of x2.
400+ //
401+ // y2 is simply y1 + yLength. Note that this can overflow, as in the
402+ // example above, which must be avoided.
403+ //
404+ // The two sequences do *not* overlap if y is entirely in the space
405+ // right of x in the relative address space. (It can't be left of it!)
406+ //
407+ // (y1 >= x2) && (y2 <= 2³²)
408+ //
409+ // Inversely, they do overlap if
410+ //
411+ // (y1 < x2) || (y2 > 2³²)
412+ //
413+ // After substituting x2 and y2 with their respective definition:
414+ //
415+ // == (y1 < xLength) || (y1 + yLength > 2³²)
416+ //
417+ // Since yLength can't be greater than the size of the address space,
418+ // the overflow can be avoided as follows:
419+ //
420+ // == (y1 < xLength) || (y1 > 2³² - yLength)
421+ //
422+ // However, 2³² cannot be stored in an unsigned 32-bit integer, so one
423+ // more change is needed to keep doing everything with unsigned 32-bit
424+ // integers:
425+ //
426+ // == (y1 < xLength) || (y1 > -yLength)
427+ //
428+ // Due to modulo arithmetic, this gives exactly same result *except* if
429+ // yLength is zero, since 2³² - 0 is 0 and not 2³². So the case
430+ // y.IsEmpty must be handled separately first.
431+ //
432+
433+ /// <summary>
434+ /// Determines whether two sequences overlap in memory.
435+ /// </summary>
436+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
437+ public static bool Overlaps < T > ( this Span < T > first , ReadOnlySpan < T > second )
438+ {
439+ return Overlaps ( ( ReadOnlySpan < T > ) first , second ) ;
440+ }
441+
442+ /// <summary>
443+ /// Determines whether two sequences overlap in memory and outputs the element offset.
444+ /// </summary>
445+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
446+ public static bool Overlaps < T > ( this Span < T > first , ReadOnlySpan < T > second , out int elementOffset )
447+ {
448+ return Overlaps ( ( ReadOnlySpan < T > ) first , second , out elementOffset ) ;
449+ }
450+
451+ /// <summary>
452+ /// Determines whether two sequences overlap in memory.
453+ /// </summary>
454+ public static bool Overlaps < T > ( this ReadOnlySpan < T > first , ReadOnlySpan < T > second )
455+ {
456+ if ( first . IsEmpty || second . IsEmpty )
457+ {
458+ return false ;
459+ }
460+
461+ IntPtr byteOffset = Unsafe . ByteOffset (
462+ ref first . DangerousGetPinnableReference ( ) ,
463+ ref second . DangerousGetPinnableReference ( ) ) ;
464+
465+ if ( Unsafe . SizeOf < IntPtr > ( ) == sizeof ( int ) )
466+ {
467+ return ( uint ) byteOffset < ( uint ) ( first . Length * Unsafe . SizeOf < T > ( ) ) ||
468+ ( uint ) byteOffset > ( uint ) - ( second . Length * Unsafe . SizeOf < T > ( ) ) ;
469+ }
470+ else
471+ {
472+ return ( ulong ) byteOffset < ( ulong ) ( ( long ) first . Length * Unsafe . SizeOf < T > ( ) ) ||
473+ ( ulong ) byteOffset > ( ulong ) - ( ( long ) second . Length * Unsafe . SizeOf < T > ( ) ) ;
474+ }
475+ }
476+
477+ /// <summary>
478+ /// Determines whether two sequences overlap in memory and outputs the element offset.
479+ /// </summary>
480+ public static bool Overlaps < T > ( this ReadOnlySpan < T > first , ReadOnlySpan < T > second , out int elementOffset )
481+ {
482+ if ( first . IsEmpty || second . IsEmpty )
483+ {
484+ elementOffset = 0 ;
485+ return false ;
486+ }
487+
488+ IntPtr byteOffset = Unsafe . ByteOffset (
489+ ref first . DangerousGetPinnableReference ( ) ,
490+ ref second . DangerousGetPinnableReference ( ) ) ;
491+
492+ if ( Unsafe . SizeOf < IntPtr > ( ) == sizeof ( int ) )
493+ {
494+ if ( ( uint ) byteOffset < ( uint ) ( first . Length * Unsafe . SizeOf < T > ( ) ) ||
495+ ( uint ) byteOffset > ( uint ) - ( second . Length * Unsafe . SizeOf < T > ( ) ) )
496+ {
497+ if ( ( int ) byteOffset % Unsafe . SizeOf < T > ( ) != 0 )
498+ ThrowHelper . ThrowArgumentException_OverlapAlignmentMismatch ( ) ;
499+
500+ elementOffset = ( int ) byteOffset / Unsafe . SizeOf < T > ( ) ;
501+ return true ;
502+ }
503+ else
504+ {
505+ elementOffset = 0 ;
506+ return false ;
507+ }
508+ }
509+ else
510+ {
511+ if ( ( ulong ) byteOffset < ( ulong ) ( ( long ) first . Length * Unsafe . SizeOf < T > ( ) ) ||
512+ ( ulong ) byteOffset > ( ulong ) - ( ( long ) second . Length * Unsafe . SizeOf < T > ( ) ) )
513+ {
514+ if ( ( long ) byteOffset % Unsafe . SizeOf < T > ( ) != 0 )
515+ ThrowHelper . ThrowArgumentException_OverlapAlignmentMismatch ( ) ;
516+
517+ elementOffset = ( int ) ( ( long ) byteOffset / Unsafe . SizeOf < T > ( ) ) ;
518+ return true ;
519+ }
520+ else
521+ {
522+ elementOffset = 0 ;
523+ return false ;
524+ }
525+ }
303526 }
304527 }
305- }
528+ }
0 commit comments