Skip to content

Commit 6a00977

Browse files
Add NativeMemory.ZeroMemory API (#69500)
* Add NativeMemory.ZeroMemory API * Replace Random.NextBytes with Span<byte>.Fill * Add ZeroMemory unit tests for zeroed region within buffer * Add remarks for ptr and byteCount parameters * Add test for ZeroMemory(null, 0) not throwing * Add unit tests for zeroing memory with a size of 0 * Move test cases with 0 byte count to separate method Co-authored-by: Stephen Toub <[email protected]>
1 parent 334989f commit 6a00977

File tree

3 files changed

+138
-0
lines changed

3 files changed

+138
-0
lines changed

src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeMemory.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,19 @@ public static unsafe partial class NativeMemory
3838
return AllocZeroed(byteCount, elementSize: 1);
3939
}
4040

41+
/// <summary>Clears a block of memory.</summary>
42+
/// <param name="ptr">A pointer to the block of memory that should be cleared.</param>
43+
/// <param name="byteCount">The size, in bytes, of the block to clear.</param>
44+
/// <remarks>
45+
/// <para>If this method is called with <paramref name="ptr" /> being <see langword="null"/> and <paramref name="byteCount" /> being <c>0</c>, it will be equivalent to a no-op.</para>
46+
/// <para>The behavior when <paramref name="ptr" /> is <see langword="null"/> and <paramref name="byteCount" /> is greater than <c>0</c> is undefined.</para>
47+
/// </remarks>
48+
[CLSCompliant(false)]
49+
public static unsafe void ZeroMemory(void* ptr, nuint byteCount)
50+
{
51+
SpanHelpers.ClearWithoutReferences(ref *(byte*)ptr, byteCount);
52+
}
53+
4154
[MethodImpl(MethodImplOptions.AggressiveInlining)]
4255
private static nuint GetByteCount(nuint elementCount, nuint elementSize)
4356
{

src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -809,6 +809,8 @@ public static void AlignedFree(void* ptr) { }
809809
public static void Free(void* ptr) { }
810810
[System.CLSCompliantAttribute(false)]
811811
public static void* Realloc(void* ptr, nuint byteCount) { throw null; }
812+
[System.CLSCompliantAttribute(false)]
813+
public static void ZeroMemory(void* ptr, nuint byteCount) { throw null; }
812814
}
813815
public readonly partial struct NFloat : System.IComparable, System.IComparable<System.Runtime.InteropServices.NFloat>, System.IEquatable<System.Runtime.InteropServices.NFloat>, System.IFormattable, System.IParsable<System.Runtime.InteropServices.NFloat>, System.ISpanFormattable, System.ISpanParsable<System.Runtime.InteropServices.NFloat>, System.Numerics.IAdditionOperators<System.Runtime.InteropServices.NFloat, System.Runtime.InteropServices.NFloat, System.Runtime.InteropServices.NFloat>, System.Numerics.IAdditiveIdentity<System.Runtime.InteropServices.NFloat, System.Runtime.InteropServices.NFloat>, System.Numerics.IBinaryFloatingPointIeee754<System.Runtime.InteropServices.NFloat>, System.Numerics.IBinaryNumber<System.Runtime.InteropServices.NFloat>, System.Numerics.IBitwiseOperators<System.Runtime.InteropServices.NFloat, System.Runtime.InteropServices.NFloat, System.Runtime.InteropServices.NFloat>, System.Numerics.IComparisonOperators<System.Runtime.InteropServices.NFloat, System.Runtime.InteropServices.NFloat>, System.Numerics.IDecrementOperators<System.Runtime.InteropServices.NFloat>, System.Numerics.IDivisionOperators<System.Runtime.InteropServices.NFloat, System.Runtime.InteropServices.NFloat, System.Runtime.InteropServices.NFloat>, System.Numerics.IEqualityOperators<System.Runtime.InteropServices.NFloat, System.Runtime.InteropServices.NFloat>, System.Numerics.IExponentialFunctions<System.Runtime.InteropServices.NFloat>, System.Numerics.IFloatingPoint<System.Runtime.InteropServices.NFloat>, System.Numerics.IFloatingPointIeee754<System.Runtime.InteropServices.NFloat>, System.Numerics.IHyperbolicFunctions<System.Runtime.InteropServices.NFloat>, System.Numerics.IIncrementOperators<System.Runtime.InteropServices.NFloat>, System.Numerics.ILogarithmicFunctions<System.Runtime.InteropServices.NFloat>, System.Numerics.IMinMaxValue<System.Runtime.InteropServices.NFloat>, System.Numerics.IModulusOperators<System.Runtime.InteropServices.NFloat, System.Runtime.InteropServices.NFloat, System.Runtime.InteropServices.NFloat>, System.Numerics.IMultiplicativeIdentity<System.Runtime.InteropServices.NFloat, System.Runtime.InteropServices.NFloat>, System.Numerics.IMultiplyOperators<System.Runtime.InteropServices.NFloat, System.Runtime.InteropServices.NFloat, System.Runtime.InteropServices.NFloat>, System.Numerics.INumber<System.Runtime.InteropServices.NFloat>, System.Numerics.INumberBase<System.Runtime.InteropServices.NFloat>, System.Numerics.IPowerFunctions<System.Runtime.InteropServices.NFloat>, System.Numerics.IRootFunctions<System.Runtime.InteropServices.NFloat>, System.Numerics.ISignedNumber<System.Runtime.InteropServices.NFloat>, System.Numerics.ISubtractionOperators<System.Runtime.InteropServices.NFloat, System.Runtime.InteropServices.NFloat, System.Runtime.InteropServices.NFloat>, System.Numerics.ITrigonometricFunctions<System.Runtime.InteropServices.NFloat>, System.Numerics.IUnaryNegationOperators<System.Runtime.InteropServices.NFloat, System.Runtime.InteropServices.NFloat>, System.Numerics.IUnaryPlusOperators<System.Runtime.InteropServices.NFloat, System.Runtime.InteropServices.NFloat>
814816
{

src/libraries/System.Runtime.InteropServices/tests/System.Runtime.InteropServices.UnitTests/System/Runtime/InteropServices/NativeMemoryTests.cs

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -435,5 +435,128 @@ public void ReallocSmallerToLargerTest()
435435

436436
NativeMemory.Free(newPtr);
437437
}
438+
439+
[Theory]
440+
[InlineData(1, 0)]
441+
[InlineData(1, 1)]
442+
[InlineData(1, 2)]
443+
[InlineData(1, 3)]
444+
[InlineData(2, 0)]
445+
[InlineData(3, 0)]
446+
[InlineData(4, 0)]
447+
[InlineData(8, 0)]
448+
[InlineData(9, 0)]
449+
[InlineData(16, 0)]
450+
[InlineData(16, 1)]
451+
[InlineData(16, 3)]
452+
[InlineData(16, 7)]
453+
[InlineData(32, 0)]
454+
[InlineData(64, 0)]
455+
[InlineData(128, 0)]
456+
[InlineData(256, 0)]
457+
[InlineData(256, 1)]
458+
[InlineData(256, 2)]
459+
[InlineData(256, 3)]
460+
[InlineData(256, 5)]
461+
[InlineData(512, 0)]
462+
[InlineData(547, 0)]
463+
[InlineData(1 * 1024, 0)]
464+
public void ZeroMemoryTest(int size, int offset)
465+
{
466+
byte* ptr = (byte*)NativeMemory.AlignedAlloc((nuint)(size + offset), 8);
467+
468+
Assert.True(ptr != null);
469+
Assert.True((nuint)ptr % 8 == 0);
470+
471+
new Span<byte>(ptr, size + offset).Fill(0b10101010);
472+
473+
NativeMemory.ZeroMemory(ptr + offset, (nuint)size);
474+
475+
Assert.Equal(-1, new Span<byte>(ptr + offset, size).IndexOfAnyExcept((byte)0));
476+
477+
NativeMemory.AlignedFree(ptr);
478+
}
479+
480+
[Theory]
481+
[InlineData(1, 0)]
482+
[InlineData(1, 1)]
483+
[InlineData(1, 2)]
484+
[InlineData(1, 3)]
485+
[InlineData(1, 44)]
486+
[InlineData(1, 367)]
487+
[InlineData(2, 0)]
488+
[InlineData(3, 0)]
489+
[InlineData(4, 0)]
490+
[InlineData(8, 0)]
491+
[InlineData(9, 0)]
492+
[InlineData(9, 2)]
493+
[InlineData(9, 111)]
494+
[InlineData(9, 289)]
495+
[InlineData(16, 0)]
496+
[InlineData(16, 1)]
497+
[InlineData(16, 3)]
498+
[InlineData(16, 7)]
499+
[InlineData(32, 0)]
500+
[InlineData(64, 0)]
501+
[InlineData(128, 0)]
502+
[InlineData(256, 0)]
503+
[InlineData(256, 1)]
504+
[InlineData(256, 2)]
505+
[InlineData(256, 3)]
506+
[InlineData(256, 5)]
507+
[InlineData(256, 67)]
508+
[InlineData(256, 143)]
509+
public void ZeroMemoryWithExactRangeTest(int size, int offset)
510+
{
511+
int headLength = offset;
512+
int bodyLength = size;
513+
int tailLength = 512 - headLength - bodyLength;
514+
int headOffset = 0;
515+
int bodyOffset = headLength;
516+
int tailOffset = headLength + bodyLength;
517+
518+
byte* ptr = (byte*)NativeMemory.AlignedAlloc(512, 8);
519+
520+
Assert.True(ptr != null);
521+
Assert.True((nuint)ptr % 8 == 0);
522+
523+
new Span<byte>(ptr, 512).Fill(0b10101010);
524+
525+
NativeMemory.ZeroMemory(ptr + bodyOffset, (nuint)bodyLength);
526+
527+
Assert.Equal(-1, new Span<byte>(ptr + headOffset, headLength).IndexOfAnyExcept((byte)0b10101010));
528+
Assert.Equal(-1, new Span<byte>(ptr + bodyOffset, bodyLength).IndexOfAnyExcept((byte)0));
529+
Assert.Equal(-1, new Span<byte>(ptr + tailOffset, tailLength).IndexOfAnyExcept((byte)0b10101010));
530+
531+
NativeMemory.AlignedFree(ptr);
532+
}
533+
534+
[Theory]
535+
[InlineData(0)]
536+
[InlineData(1)]
537+
[InlineData(167)]
538+
public void ZeroMemoryWithSizeEqualTo0ShouldNoOpTest(int offset)
539+
{
540+
byte* ptr = (byte*)NativeMemory.AlignedAlloc(512, 8);
541+
542+
Assert.True(ptr != null);
543+
Assert.True((nuint)ptr % 8 == 0);
544+
545+
new Span<byte>(ptr, 512).Fill(0b10101010);
546+
547+
NativeMemory.ZeroMemory(ptr + offset, 0);
548+
549+
Assert.Equal(-1, new Span<byte>(ptr, 512).IndexOfAnyExcept((byte)0b10101010));
550+
551+
NativeMemory.AlignedFree(ptr);
552+
}
553+
554+
[Fact]
555+
public void ZeroMemoryWithNullPointerAndZeroByteCountTest()
556+
{
557+
NativeMemory.ZeroMemory(null, 0);
558+
559+
// This test method just needs to check that no exceptions are thrown
560+
}
438561
}
439562
}

0 commit comments

Comments
 (0)