diff --git a/src/Roaring.Net/CRoaring/FrozenRoaring32Bitmap.cs b/src/Roaring.Net/CRoaring/FrozenRoaring32Bitmap.cs index ceef602..e067940 100644 --- a/src/Roaring.Net/CRoaring/FrozenRoaring32Bitmap.cs +++ b/src/Roaring.Net/CRoaring/FrozenRoaring32Bitmap.cs @@ -336,6 +336,20 @@ public bool ContainsBulk(BulkContext context, uint value) /// Thrown when the size is too small to write the bitmap. public void CopyTo(uint[] buffer) => _bitmap.CopyTo(buffer); + /// + /// Writes current bitmap to the given in the parameter. + /// + /// The in which the bitmap will be written. + /// Thrown when the size is too small to write the bitmap. + public void CopyTo(Memory buffer) => _bitmap.CopyTo(buffer); + + /// + /// Writes current bitmap to the given in the parameter. + /// + /// The in which the bitmap will be written. + /// Thrown when the size is too small to write the bitmap. + public void CopyTo(Span buffer) => _bitmap.CopyTo(buffer); + /// /// Gets enumerator that returns the values contained in the bitmap. /// diff --git a/src/Roaring.Net/CRoaring/IReadOnlyRoaring32Bitmap.cs b/src/Roaring.Net/CRoaring/IReadOnlyRoaring32Bitmap.cs index 9203442..290f3dc 100644 --- a/src/Roaring.Net/CRoaring/IReadOnlyRoaring32Bitmap.cs +++ b/src/Roaring.Net/CRoaring/IReadOnlyRoaring32Bitmap.cs @@ -77,6 +77,10 @@ internal interface IReadOnlyRoaring32Bitmap : IDisposable void CopyTo(uint[] buffer); + void CopyTo(Memory buffer); + + void CopyTo(Span buffer); + IEnumerable Values { get; } uint[] ToArray(); diff --git a/src/Roaring.Net/CRoaring/NativeMethods.cs b/src/Roaring.Net/CRoaring/NativeMethods.cs index 9bb461f..2c86e83 100644 --- a/src/Roaring.Net/CRoaring/NativeMethods.cs +++ b/src/Roaring.Net/CRoaring/NativeMethods.cs @@ -535,6 +535,14 @@ internal static unsafe partial class NativeMethods public static extern void roaring_bitmap_to_uint32_array(IntPtr bitmap, [Out] uint[] values); #endif +#if NET7_0_OR_GREATER + [LibraryImport("roaring", EntryPoint = "roaring_bitmap_to_uint32_array")] + public static partial void roaring_bitmap_to_uint32_array(IntPtr bitmap, Span values); +#else + [DllImport("roaring")] + public static extern void roaring_bitmap_to_uint32_array(IntPtr bitmap, Span values); +#endif + #if NET7_0_OR_GREATER [LibraryImport("roaring", EntryPoint = "roaring_bitmap_serialize")] public static partial nuint roaring_bitmap_serialize(IntPtr bitmap, [Out] byte[] buffer); diff --git a/src/Roaring.Net/CRoaring/Roaring32Bitmap.cs b/src/Roaring.Net/CRoaring/Roaring32Bitmap.cs index 9364495..25fb849 100644 --- a/src/Roaring.Net/CRoaring/Roaring32Bitmap.cs +++ b/src/Roaring.Net/CRoaring/Roaring32Bitmap.cs @@ -846,6 +846,28 @@ public void CopyTo(uint[] buffer) NativeMethods.roaring_bitmap_to_uint32_array(Pointer, buffer); } + /// + /// Writes current bitmap to the given in the parameter. + /// + /// The in which the bitmap will be written. + /// Thrown when the size is too small to write the bitmap. + public void CopyTo(Memory buffer) => CopyTo(buffer.Span); + + /// + /// Writes current bitmap to the given in the parameter. + /// + /// The in which the bitmap will be written. + /// Thrown when the size is too small to write the bitmap. + public void CopyTo(Span buffer) + { + if ((ulong)buffer.Length < Count) + { + throw new ArgumentOutOfRangeException(nameof(buffer), buffer.Length, ExceptionMessages.BufferSizeIsTooSmall); + } + + NativeMethods.roaring_bitmap_to_uint32_array(Pointer, buffer); + } + /// /// Gets enumerator that returns the values contained in the bitmap. /// diff --git a/test/Roaring.Net.Tests/CRoaring/Roaring32BitmapTests/CollectionTests.cs b/test/Roaring.Net.Tests/CRoaring/Roaring32BitmapTests/CollectionTests.cs index 296bcb9..0da7897 100644 --- a/test/Roaring.Net.Tests/CRoaring/Roaring32BitmapTests/CollectionTests.cs +++ b/test/Roaring.Net.Tests/CRoaring/Roaring32BitmapTests/CollectionTests.cs @@ -24,7 +24,7 @@ public void ToArray_Always_ReturnsArrayWithExpectedValues(uint[] expected, IRoar } } - public class CopyTo + public class CopyToArray { [Theory] [InlineTestObject(new uint[] { })] @@ -91,6 +91,140 @@ public void CopyTo_OutputCollectionSizeGreaterThanNumberOfValues_ReturnsFilledCo } } + public class CopyToMemory + { + [Theory] + [InlineTestObject(new uint[] { })] + [InlineTestObject(new uint[] { uint.MaxValue })] + [InlineTestObject(new uint[] { 0, 1, 2, 3, 4 })] + public void CopyTo_OutputCollectionSizeEqualToNumberOfValues_ReturnsFilledCollection(uint[] expected, IRoaring32BitmapTestObjectFactory factory) + { + // Arrange + using IRoaring32BitmapTestObject testObject = factory.GetFromValues(expected); + uint[] actual = new uint[expected.Length]; + + // Act + testObject.ReadOnlyBitmap.CopyTo(actual.AsMemory()); + + // Assert + Assert.Equal(expected, actual); + } + + [Theory] + [InlineTestObject] + public void CopyTo_OutputCollectionIsEmptyWhenBitmapHasValues_ThrowsArgumentOutOfRangeException(IRoaring32BitmapTestObjectFactory factory) + { + // Arrange + using IRoaring32BitmapTestObject testObject = factory.GetDefault(); + uint[] actual = []; + + // Act && Assert + Assert.Throws(() => + { + testObject.ReadOnlyBitmap.CopyTo(actual.AsMemory()); + }); + } + + [Theory] + [InlineTestObject] + public void CopyTo_OutputCollectionSizeLowerThanNumberOfValues_ThrowsArgumentOutOfRangeException(IRoaring32BitmapTestObjectFactory factory) + { + // Arrange + var input = new uint[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + using IRoaring32BitmapTestObject testObject = factory.GetFromValues(input); + + // Act && Assert + Assert.Throws(() => + { + testObject.ReadOnlyBitmap.CopyTo(new uint[input.Length - 5].AsMemory()); + }); + } + + [Theory] + [InlineTestObject] + public void CopyTo_OutputCollectionSizeGreaterThanNumberOfValues_ReturnsFilledCollectionFromBeginning(IRoaring32BitmapTestObjectFactory factory) + { + // Arrange + var expected = new uint[] { 0, 1, 2, 3, 4, 0, 0, 0, 0, 0 }; + var input = expected[..5]; + using IRoaring32BitmapTestObject testObject = factory.GetFromValues(input); + uint[] actual = new uint[input.Length + 5]; + + // Act + testObject.ReadOnlyBitmap.CopyTo(actual.AsMemory()); + + // Assert + Assert.Equal(expected, actual); + } + } + + public class CopyToSpan + { + [Theory] + [InlineTestObject(new uint[] { })] + [InlineTestObject(new uint[] { uint.MaxValue })] + [InlineTestObject(new uint[] { 0, 1, 2, 3, 4 })] + public void CopyTo_OutputCollectionSizeEqualToNumberOfValues_ReturnsFilledCollection(uint[] expected, IRoaring32BitmapTestObjectFactory factory) + { + // Arrange + using IRoaring32BitmapTestObject testObject = factory.GetFromValues(expected); + uint[] actual = new uint[expected.Length]; + + // Act + testObject.ReadOnlyBitmap.CopyTo(actual.AsSpan()); + + // Assert + Assert.Equal(expected, actual); + } + + [Theory] + [InlineTestObject] + public void CopyTo_OutputCollectionIsEmptyWhenBitmapHasValues_ThrowsArgumentOutOfRangeException(IRoaring32BitmapTestObjectFactory factory) + { + // Arrange + using IRoaring32BitmapTestObject testObject = factory.GetDefault(); + uint[] actual = []; + + // Act && Assert + Assert.Throws(() => + { + testObject.ReadOnlyBitmap.CopyTo(actual.AsSpan()); + }); + } + + [Theory] + [InlineTestObject] + public void CopyTo_OutputCollectionSizeLowerThanNumberOfValues_ThrowsArgumentOutOfRangeException(IRoaring32BitmapTestObjectFactory factory) + { + // Arrange + var input = new uint[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + using IRoaring32BitmapTestObject testObject = factory.GetFromValues(input); + + // Act && Assert + Assert.Throws(() => + { + testObject.ReadOnlyBitmap.CopyTo(new uint[input.Length - 5].AsSpan()); + }); + } + + [Theory] + [InlineTestObject] + public void CopyTo_OutputCollectionSizeGreaterThanNumberOfValues_ReturnsFilledCollectionFromBeginning(IRoaring32BitmapTestObjectFactory factory) + { + // Arrange + var expected = new uint[] { 0, 1, 2, 3, 4, 0, 0, 0, 0, 0 }; + var input = expected[..5]; + using IRoaring32BitmapTestObject testObject = factory.GetFromValues(input); + uint[] actual = new uint[input.Length + 5]; + + // Act + testObject.ReadOnlyBitmap.CopyTo(actual.AsSpan()); + + // Assert + Assert.Equal(expected, actual); + } + } + public class Take { [Theory]