/// <summary> /// Gets array if the slice is over an array, otherwise gets a pointer to memory. /// </summary> /// <returns>true if it's a span over an array; otherwise false (if over a pointer)</returns> /// <remarks>This method can be used for interop, while we are waiting for proper pinning support. Make sure the array contents are read-only.</remarks> public unsafe bool TryGetArrayElseGetPointer(out ArraySegment <T> array, out void *pointer) { var a = Object as T[]; if (a == null) { array = new ArraySegment <T>(); pointer = UnsafeUtilities.ComputeAddress(Object, Offset).ToPointer(); return(false); } var offsetToData = SpanHelpers <T> .OffsetToArrayData; var index = (int)((Offset.ToUInt32() - offsetToData) / UnsafeUtilities.SizeOf <T>()); array = new ArraySegment <T>(a, index, Length); pointer = null; return(true); }
/// <summary> /// platform independent fast memory comparison /// for x64 it is as fast as memcmp of msvcrt.dll, for x86 it is up to two times faster!! /// </summary> internal static bool MemCmp <[Primitive] T>(Span <T> first, Span <T> second) where T : struct { if (first.Length != second.Length) { return(false); } unsafe { // prevent GC from moving memory var firstPinnedHandle = GCHandle.Alloc(first.Object, GCHandleType.Pinned); var secondPinnedHandle = GCHandle.Alloc(second.Object, GCHandleType.Pinned); try { byte *firstPointer = (byte *)UnsafeUtilities.ComputeAddress(first.Object, first.Offset).ToPointer(); byte *secondPointer = (byte *)UnsafeUtilities.ComputeAddress(second.Object, second.Offset).ToPointer(); int step = sizeof(void *) * 5; int totalBytesCount = first.Length * UnsafeUtilities.SizeOf <T>(); byte *firstPointerLimit = firstPointer + (totalBytesCount - step); if (totalBytesCount > step) { while (firstPointer < firstPointerLimit) { // IMPORTANT: in order to get HUGE benefits of loop unrolling on x86 we use break instead of return if (*((void **)firstPointer + 0) != *((void **)secondPointer + 0)) { break; } if (*((void **)firstPointer + 1) != *((void **)secondPointer + 1)) { break; } if (*((void **)firstPointer + 2) != *((void **)secondPointer + 2)) { break; } if (*((void **)firstPointer + 3) != *((void **)secondPointer + 3)) { break; } if (*((void **)firstPointer + 4) != *((void **)secondPointer + 4)) { break; } firstPointer += step; secondPointer += step; } if (firstPointer < firstPointerLimit) // the upper loop ended with break; { return(false); } } firstPointerLimit += step; // lets check the remaining bytes while (firstPointer < firstPointerLimit) { if (*firstPointer != *secondPointer) { break; } ++firstPointer; ++secondPointer; } return(firstPointer == firstPointerLimit); } finally { if (firstPinnedHandle.IsAllocated) { firstPinnedHandle.Free(); } if (secondPinnedHandle.IsAllocated) { secondPinnedHandle.Free(); } } } }