Skip to content

Commit e0f6a16

Browse files
committed
added CloneWithOffset/CloneWithNegativeOffset/AddOffset/SubtractOffset, OverwriteWith, RemoveRunCompression for 64-bit bitmap; fixed descriptions
1 parent 96de07c commit e0f6a16

9 files changed

Lines changed: 375 additions & 6 deletions

File tree

src/Roaring.Net/CRoaring/FrozenRoaring32Bitmap.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -379,18 +379,20 @@ public bool ContainsBulk(BulkContext context, uint value)
379379
public Roaring32Bitmap ToBitmap() => _bitmap.Clone();
380380

381381
/// <summary>
382-
/// Converts <see cref="FrozenRoaring32Bitmap"/> to the <see cref="Roaring32Bitmap"/> from the given offset.
382+
/// Converts <see cref="FrozenRoaring32Bitmap"/> to the <see cref="Roaring32Bitmap"/> and adds an offset.
383383
/// </summary>
384+
/// <param name="offset">The offset to be added to the bitmap when converting.</param>
384385
/// <returns>Instance of the <see cref="Roaring32Bitmap"/> class with the same values as the current bitmap from the given offset.</returns>
385386
/// <exception cref="InvalidOperationException">Thrown when unable to allocate bitmap.</exception>
387+
/// <remarks>Values that overflow or underflow are dropped.</remarks>
386388
public Roaring32Bitmap ToBitmapWithOffset(long offset) => _bitmap.CloneWithOffset(offset);
387389

388390
/// <summary>
389391
/// Takes the given number of values from the current bitmap and puts them into an array.
390392
/// </summary>
391393
/// <param name="count">Number of values to take from the bitmap.</param>
392394
/// <returns>An array containing the given number of values from the bitmap.</returns>
393-
/// <remarks>If the bitmap contains fewer values than the given number then the array will be adjusted to the number of values.</remarks>
395+
/// <remarks>If the bitmap contains fewer values than the given number, then the array will be adjusted to the number of values.</remarks>
394396
public uint[] Take(ulong count) => _bitmap.Take(count);
395397

396398
/// <summary>

src/Roaring.Net/CRoaring/FrozenRoaring64Bitmap.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,24 @@ public bool ContainsBulk(BulkContext64 context, ulong value)
329329
/// <exception cref="InvalidOperationException">Thrown when unable to allocate bitmap.</exception>
330330
public Roaring64Bitmap ToBitmap() => _bitmap.Clone();
331331

332+
/// <summary>
333+
/// Converts <see cref="FrozenRoaring64Bitmap"/> to the <see cref="Roaring64Bitmap"/> and adds an offset.
334+
/// </summary>
335+
/// <param name="offset">The offset to be added to the bitmap when converting.</param>
336+
/// <returns>An instance of the <see cref="Roaring64Bitmap"/> class with values shifted relative to the current bitmap by the specified offset.</returns>
337+
/// <exception cref="InvalidOperationException">Thrown when unable to allocate bitmap.</exception>
338+
/// <remarks>Values that overflow are dropped.</remarks>
339+
public Roaring64Bitmap ToBitmapWithOffset(ulong offset) => _bitmap.CloneWithOffset(offset);
340+
341+
/// <summary>
342+
/// Converts <see cref="FrozenRoaring64Bitmap"/> to the <see cref="Roaring64Bitmap"/> and subtracts an offset.
343+
/// </summary>
344+
/// <param name="offset">The offset to be subtracted from the bitmap when converting.</param>
345+
/// <returns>An instance of the <see cref="Roaring64Bitmap"/> class with values shifted relative to the current bitmap by the specified offset.</returns>
346+
/// <exception cref="InvalidOperationException">Thrown when unable to allocate bitmap.</exception>
347+
/// <remarks>Values that underflow are dropped.</remarks>
348+
public Roaring64Bitmap ToBitmapWithNegativeOffset(ulong offset) => _bitmap.CloneWithNegativeOffset(offset);
349+
332350
/// <summary>
333351
/// Takes the given number of values from the current bitmap and puts them into an array.
334352
/// </summary>

src/Roaring.Net/CRoaring/NativeMethods.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,14 @@ internal static unsafe partial class NativeMethods
7979
public static extern bool roaring_bitmap_overwrite(IntPtr destination, IntPtr source);
8080
#endif
8181

82+
#if NET7_0_OR_GREATER
83+
[LibraryImport("roaring", EntryPoint = "roaring64_bitmap_overwrite")]
84+
public static partial void roaring64_bitmap_overwrite(IntPtr destination, IntPtr source);
85+
#else
86+
[DllImport("roaring")]
87+
public static extern void roaring64_bitmap_overwrite(IntPtr destination, IntPtr source);
88+
#endif
89+
8290
#if NET7_0_OR_GREATER
8391
[LibraryImport("roaring", EntryPoint = "roaring_bitmap_free")]
8492
public static partial void roaring_bitmap_free(IntPtr bitmap);
@@ -248,6 +256,14 @@ internal static unsafe partial class NativeMethods
248256
public static extern IntPtr roaring_bitmap_add_offset(IntPtr bitmap, long offset);
249257
#endif
250258

259+
#if NET7_0_OR_GREATER
260+
[LibraryImport("roaring", EntryPoint = "roaring64_bitmap_add_offset_signed")]
261+
public static partial IntPtr roaring64_bitmap_add_offset_signed(IntPtr bitmap, [MarshalAs(UnmanagedType.I1)] bool positive, ulong offset);
262+
#else
263+
[DllImport("roaring")]
264+
public static extern IntPtr roaring64_bitmap_add_offset_signed(IntPtr bitmap, bool positive, ulong offset);
265+
#endif
266+
251267
#if NET7_0_OR_GREATER
252268
[LibraryImport("roaring", EntryPoint = "roaring_bitmap_remove")]
253269
public static partial void roaring_bitmap_remove(IntPtr bitmap, uint value);
@@ -878,6 +894,15 @@ internal static unsafe partial class NativeMethods
878894
public static extern bool roaring_bitmap_remove_run_compression(IntPtr bitmap);
879895
#endif
880896

897+
[return: MarshalAs(UnmanagedType.I1)]
898+
#if NET7_0_OR_GREATER
899+
[LibraryImport("roaring", EntryPoint = "roaring64_bitmap_remove_run_compression")]
900+
public static partial bool roaring64_bitmap_remove_run_compression(IntPtr bitmap);
901+
#else
902+
[DllImport("roaring")]
903+
public static extern bool roaring64_bitmap_remove_run_compression(IntPtr bitmap);
904+
#endif
905+
881906
#if NET7_0_OR_GREATER
882907
[LibraryImport("roaring", EntryPoint = "roaring_bitmap_shrink_to_fit")]
883908
public static partial nuint roaring_bitmap_shrink_to_fit(IntPtr bitmap);

src/Roaring.Net/CRoaring/Roaring32Bitmap.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -176,11 +176,12 @@ protected override void Dispose(bool disposing)
176176
public Roaring32Bitmap Clone() => new(NativeMethods.roaring_bitmap_copy(Pointer));
177177

178178
/// <summary>
179-
/// Copies the bitmap from the given offset.
179+
/// Copies the bitmap and adds an offset.
180180
/// </summary>
181-
/// <param name="offset">The position in the bitmap from which to start copying data.</param>
182-
/// <returns>Instance of the <see cref="Roaring32Bitmap"/> class with the same values as the current bitmap from the given offset.</returns>
181+
/// <param name="offset">The offset to be added to the bitmap when copying data.</param>
182+
/// <returns>An instance of the <see cref="Roaring32Bitmap"/> class with values shifted relative to the current bitmap by the specified offset.</returns>
183183
/// <exception cref="InvalidOperationException">Thrown when unable to allocate bitmap.</exception>
184+
/// <remarks>Values that overflow or underflow are dropped.</remarks>
184185
public Roaring32Bitmap CloneWithOffset(long offset)
185186
=> new(NativeMethods.roaring_bitmap_add_offset(Pointer, offset));
186187

@@ -257,6 +258,7 @@ public void AddRange(uint start, uint end)
257258
/// <param name="offset">The offset that will be added to all values.</param>
258259
/// <remarks>This method allocates a new bitmap.</remarks>
259260
/// <exception cref="InvalidOperationException">Thrown when unable to allocate bitmap.</exception>
261+
/// <remarks>Values that overflow or underflow are dropped.</remarks>
260262
public void AddOffset(long offset)
261263
{
262264
IntPtr previousPtr = Pointer;

src/Roaring.Net/CRoaring/Roaring64Bitmap.cs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,37 @@ protected override void Dispose(bool disposing)
170170
/// <exception cref="InvalidOperationException">Thrown when unable to allocate bitmap.</exception>
171171
public Roaring64Bitmap Clone() => new(NativeMethods.roaring64_bitmap_copy(Pointer));
172172

173+
/// <summary>
174+
/// Copies the bitmap and adds an offset.
175+
/// </summary>
176+
/// <param name="offset">The offset to be added to the bitmap when copying data.</param>
177+
/// <returns>An instance of the <see cref="Roaring64Bitmap"/> class with values shifted relative to the current bitmap by the specified offset.</returns>
178+
/// <exception cref="InvalidOperationException">Thrown when unable to allocate bitmap.</exception>
179+
/// <remarks>Values that overflow are dropped.</remarks>
180+
public Roaring64Bitmap CloneWithOffset(ulong offset)
181+
=> new(NativeMethods.roaring64_bitmap_add_offset_signed(Pointer, true, offset));
182+
183+
/// <summary>
184+
/// Copies the bitmap and subtracts an offset.
185+
/// </summary>
186+
/// <param name="offset">The offset to be subtracted from the bitmap when copying data.</param>
187+
/// <returns>An instance of the <see cref="Roaring64Bitmap"/> class with values shifted relative to the current bitmap by the specified offset.</returns>
188+
/// <exception cref="InvalidOperationException">Thrown when unable to allocate bitmap.</exception>
189+
/// <remarks>Values that underflow are dropped.</remarks>
190+
public Roaring64Bitmap CloneWithNegativeOffset(ulong offset)
191+
=> new(NativeMethods.roaring64_bitmap_add_offset_signed(Pointer, false, offset));
192+
193+
/// <summary>
194+
/// Overwrites the current bitmap with the bitmap given in the <paramref name="source"/> parameter. <br/>
195+
/// The content of the current bitmap will be deleted.
196+
/// </summary>
197+
/// <param name="source">Bitmap that will be written in place of the current bitmap.</param>
198+
/// <remarks>
199+
/// The <see cref="OverwriteWith"/> method can save on memory allocations compared to the <see cref="Clone"/> method. <br/>
200+
/// On failure, the current bitmap is left in a valid, empty state (all stored values are deleted).
201+
/// </remarks>
202+
public void OverwriteWith(Roaring64BitmapBase source) => NativeMethods.roaring64_bitmap_overwrite(Pointer, source.Pointer);
203+
173204
/// <summary>
174205
/// Adds a value to the bitmap.
175206
/// </summary>
@@ -225,6 +256,34 @@ public void AddRange(ulong start, ulong end)
225256
NativeMethods.roaring64_bitmap_add_range_closed(Pointer, start, end);
226257
}
227258

259+
/// <summary>
260+
/// Adds an offset to all values stored in the bitmap.
261+
/// </summary>
262+
/// <param name="offset">The offset that will be added to all values.</param>
263+
/// <remarks>This method allocates a new bitmap.</remarks>
264+
/// <exception cref="InvalidOperationException">Thrown when unable to allocate bitmap.</exception>
265+
/// <remarks>Values that overflow are dropped.</remarks>
266+
public void AddOffset(ulong offset)
267+
{
268+
IntPtr previousPtr = Pointer;
269+
Pointer = CheckBitmapPointer(NativeMethods.roaring64_bitmap_add_offset_signed(Pointer, true, offset));
270+
NativeMethods.roaring64_bitmap_free(previousPtr);
271+
}
272+
273+
/// <summary>
274+
/// Subtract an offset from all values stored in the bitmap.
275+
/// </summary>
276+
/// <param name="offset">The offset that will be subtracted from all values.</param>
277+
/// <remarks>This method allocates a new bitmap.</remarks>
278+
/// <exception cref="InvalidOperationException">Thrown when unable to allocate bitmap.</exception>
279+
/// <remarks>Values that underflow are dropped.</remarks>
280+
public void SubtractOffset(ulong offset)
281+
{
282+
IntPtr previousPtr = Pointer;
283+
Pointer = CheckBitmapPointer(NativeMethods.roaring64_bitmap_add_offset_signed(Pointer, false, offset));
284+
NativeMethods.roaring64_bitmap_free(previousPtr);
285+
}
286+
228287
/// <summary>
229288
/// Adds value to the bitmap using context from a previous bulk operation to optimize the addition process.
230289
/// </summary>
@@ -748,6 +807,13 @@ public double GetJaccardIndex(Roaring64BitmapBase bitmap)
748807
public bool Optimize()
749808
=> NativeMethods.roaring64_bitmap_run_optimize(Pointer);
750809

810+
/// <summary>
811+
/// Removes run-length encoding even when it is more space efficient.
812+
/// </summary>
813+
/// <returns><c>true</c> if remove operation has been performed; otherwise, <c>false</c>.</returns>
814+
public bool RemoveRunCompression()
815+
=> NativeMethods.roaring64_bitmap_remove_run_compression(Pointer);
816+
751817
/// <summary>
752818
/// Tries to reallocate memory to reduce the memory usage.
753819
/// </summary>

test/Roaring.Net.Tests/CRoaring/FrozenRoaring64BitmapTests/ConvertTests.cs

Lines changed: 90 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
using System.Linq;
2-
using System.Runtime.InteropServices;
32
using Roaring.Net.CRoaring;
43
using Roaring.Net.Tests.CRoaring.Roaring64BitmapTests;
54
using Xunit;
@@ -45,4 +44,94 @@ public void ToBitmap_DestroyOriginalBitmap_NewBitmapIsSillUsable()
4544
Assert.True(actual.IsEmpty);
4645
}
4746
}
47+
48+
public class ToBitmapWithOffset
49+
{
50+
[Theory]
51+
[InlineData(new ulong[] { }, new ulong[] { }, 10)]
52+
[InlineData(new ulong[] { 0, 1, 2, 3, 4 }, new ulong[] { 5, 6, 7, 8, 9 }, 5)]
53+
[InlineData(new ulong[] { 0, 2, 4, 6, 8 }, new ulong[] { 10, 12, 14, 16, 18 }, 10)]
54+
[InlineData(new ulong[] { 0, 1, 2, 3, 4, ulong.MaxValue }, new ulong[] { 1, 2, 3, 4, 5 }, 1)]
55+
[InlineData(new ulong[] { ulong.MaxValue - 1, ulong.MaxValue }, new ulong[] { ulong.MaxValue }, 1)]
56+
[InlineData(new ulong[] { 0 }, new ulong[] { ulong.MaxValue }, ulong.MaxValue)]
57+
public void ToBitmapWithOffset_AddsValueToBitmapValues_ReturnsNewBitmapWithExpectedValues(ulong[] values, ulong[] expected, ulong offset)
58+
{
59+
// Arrange
60+
using FrozenRoaring64BitmapTestObject testObject = FrozenRoaring64BitmapTestObjectFactory.Default.GetFromValues(values);
61+
62+
// Act
63+
using Roaring64Bitmap actualBitmap = testObject.Bitmap.ToBitmapWithOffset(offset);
64+
65+
// Assert
66+
var actual = actualBitmap.Values.ToList();
67+
Assert.Equal(expected, actual);
68+
Assert.Equal(testObject.Bitmap.Values, values);
69+
}
70+
71+
[Fact]
72+
public void ToBitmapWithOffset_DestroyOriginalBitmap_NewBitmapIsSillUsable()
73+
{
74+
// Act
75+
FrozenRoaring64BitmapTestObject testObject = FrozenRoaring64BitmapTestObjectFactory.Default.GetDefault();
76+
var expectedValues = testObject.Bitmap.Values.ToList();
77+
78+
// Act
79+
Roaring64Bitmap actual = testObject.Bitmap.ToBitmapWithOffset(0);
80+
testObject.Dispose();
81+
82+
// Assert
83+
Assert.Equal(expectedValues, actual.Values);
84+
85+
using Roaring64BitmapTestObject opBitmap = Roaring64BitmapTestObjectFactory.Default.GetForCount(100);
86+
actual.IOr(opBitmap.Bitmap);
87+
Assert.Equal(expectedValues.Concat(opBitmap.Values).OrderBy(x => x).Distinct(), actual.Values);
88+
89+
actual.Clear();
90+
Assert.True(actual.IsEmpty);
91+
}
92+
}
93+
94+
public class ToBitmapWithNegativeOffset
95+
{
96+
[Theory]
97+
[InlineData(new ulong[] { 0, 1, 2, 3, 4 }, new ulong[] { 0, 1, 2 }, 2)]
98+
[InlineData(new ulong[] { 0, 1, 2, 3, 4 }, new ulong[] { }, 5)]
99+
[InlineData(new ulong[] { ulong.MaxValue }, new ulong[] { 0 }, ulong.MaxValue)]
100+
[InlineData(new ulong[] { ulong.MaxValue }, new ulong[] { ulong.MaxValue - 1 }, 1)]
101+
public void ToBitmapWithNegativeOffset_SubtractValueFromBitmapValues_ReturnsNewBitmapWithExpectedValues(ulong[] values, ulong[] expected, ulong offset)
102+
{
103+
// Arrange
104+
using FrozenRoaring64BitmapTestObject testObject = FrozenRoaring64BitmapTestObjectFactory.Default.GetFromValues(values);
105+
106+
// Act
107+
using Roaring64Bitmap actualBitmap = testObject.Bitmap.ToBitmapWithNegativeOffset(offset);
108+
109+
// Assert
110+
var actual = actualBitmap.Values.ToList();
111+
Assert.Equal(expected, actual);
112+
Assert.Equal(testObject.Bitmap.Values, values);
113+
}
114+
115+
[Fact]
116+
public void ToBitmapWithNegativeOffset_DestroyOriginalBitmap_NewBitmapIsSillUsable()
117+
{
118+
// Act
119+
FrozenRoaring64BitmapTestObject testObject = FrozenRoaring64BitmapTestObjectFactory.Default.GetDefault();
120+
var expectedValues = testObject.Bitmap.Values.ToList();
121+
122+
// Act
123+
Roaring64Bitmap actual = testObject.Bitmap.ToBitmapWithNegativeOffset(0);
124+
testObject.Dispose();
125+
126+
// Assert
127+
Assert.Equal(expectedValues, actual.Values);
128+
129+
using Roaring64BitmapTestObject opBitmap = Roaring64BitmapTestObjectFactory.Default.GetForCount(100);
130+
actual.IOr(opBitmap.Bitmap);
131+
Assert.Equal(expectedValues.Concat(opBitmap.Values).OrderBy(x => x).Distinct(), actual.Values);
132+
133+
actual.Clear();
134+
Assert.True(actual.IsEmpty);
135+
}
136+
}
48137
}

test/Roaring.Net.Tests/CRoaring/Roaring64BitmapTests/AddTests.cs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,50 @@ public void AddRange_CorrectRange_BitmapContainsExpectedValues(ulong start, ulon
236236
}
237237
}
238238

239+
public class AddOffset
240+
{
241+
[Theory]
242+
[InlineData(new ulong[] { }, new ulong[] { }, 10)]
243+
[InlineData(new ulong[] { 0, 1, 2, 3, 4 }, new ulong[] { 5, 6, 7, 8, 9 }, 5)]
244+
[InlineData(new ulong[] { 0, 2, 4, 6, 8 }, new ulong[] { 10, 12, 14, 16, 18 }, 10)]
245+
[InlineData(new ulong[] { 0, 1, 2, 3, 4, ulong.MaxValue }, new ulong[] { 1, 2, 3, 4, 5 }, 1)]
246+
[InlineData(new ulong[] { ulong.MaxValue - 1, ulong.MaxValue }, new ulong[] { ulong.MaxValue }, 1)]
247+
[InlineData(new ulong[] { 0 }, new ulong[] { ulong.MaxValue }, ulong.MaxValue)]
248+
public void AddOffset_AddsValueToBitmapValues_BitmapContainsExpectedValues(ulong[] values, ulong[] expected, ulong offset)
249+
{
250+
// Arrange
251+
using Roaring64BitmapTestObject testObject = Roaring64BitmapTestObjectFactory.Default.GetFromValues(values);
252+
253+
// Act
254+
testObject.Bitmap.AddOffset(offset);
255+
256+
// Assert
257+
var actual = testObject.Bitmap.Values.ToList();
258+
Assert.Equal(expected, actual);
259+
}
260+
}
261+
262+
public class SubtractOffset
263+
{
264+
[Theory]
265+
[InlineData(new ulong[] { 0, 1, 2, 3, 4 }, new ulong[] { 0, 1, 2 }, 2)]
266+
[InlineData(new ulong[] { 0, 1, 2, 3, 4 }, new ulong[] { }, 5)]
267+
[InlineData(new ulong[] { ulong.MaxValue }, new ulong[] { 0 }, ulong.MaxValue)]
268+
[InlineData(new ulong[] { ulong.MaxValue }, new ulong[] { ulong.MaxValue - 1 }, 1)]
269+
public void SubtractOffset_SubtractValueFromBitmapValues_BitmapContainsExpectedValues(ulong[] values, ulong[] expected, ulong offset)
270+
{
271+
// Arrange
272+
using Roaring64BitmapTestObject testObject = Roaring64BitmapTestObjectFactory.Default.GetFromValues(values);
273+
274+
// Act
275+
testObject.Bitmap.SubtractOffset(offset);
276+
277+
// Assert
278+
var actual = testObject.Bitmap.Values.ToList();
279+
Assert.Equal(expected, actual);
280+
}
281+
}
282+
239283
public class AddBulk
240284
{
241285
[Fact]

0 commit comments

Comments
 (0)