Skip to content

Commit f47f761

Browse files
maliysergeySerhiiMalyiSergiusTheBest
authored
Add a wrapper for RTL_BITMAP (#6)
* Add a wrapper for RTL_BITMAP * Fix Bitmap and BitmapRangeIterator, add basic tests --------- Co-authored-by: Serhii Malyi <maliy.sergey@apriorit.com> Co-authored-by: Sergey Podobry <sergius@apriorit.com>
1 parent 9a5dffe commit f47f761

File tree

5 files changed

+287
-0
lines changed

5 files changed

+287
-0
lines changed

include/kf/Bitmap.h

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
#pragma once
2+
#include <kf/stl/new>
3+
#include <kf/BitmapRangeIterator.h>
4+
5+
namespace kf
6+
{
7+
//////////////////////////////////////////////////////////////////////////
8+
// Bitmap - effectively works with set of bits
9+
10+
template<POOL_TYPE poolType>
11+
class Bitmap
12+
{
13+
public:
14+
Bitmap() noexcept = default;
15+
16+
~Bitmap() noexcept
17+
{
18+
deinitialize();
19+
}
20+
21+
// Non-copyable
22+
Bitmap(const Bitmap&) = delete;
23+
Bitmap& operator=(const Bitmap&) = delete;
24+
25+
// Movable
26+
Bitmap(Bitmap&& other) noexcept : m_header(other.m_header)
27+
{
28+
other.m_header = {};
29+
}
30+
31+
Bitmap& operator=(Bitmap&& other) noexcept
32+
{
33+
if (&other != this)
34+
{
35+
m_header = other.m_header;
36+
other.m_header = {};
37+
}
38+
39+
return *this;
40+
}
41+
42+
// IRQL <= APC_LEVEL for PagedPool and on Windows 7 and earlier
43+
[[nodiscard]] NTSTATUS initialize(ULONG size) noexcept
44+
{
45+
deinitialize();
46+
47+
// Convert bits to bytes and align up to sizeof(ULONG) bytes
48+
const ULONG bufferSize = ALIGN_UP_BY(size, sizeof(ULONG) * CHAR_BIT) / CHAR_BIT;
49+
50+
auto buffer = operator new(bufferSize, poolType);
51+
if (!buffer)
52+
{
53+
return STATUS_INSUFFICIENT_RESOURCES;
54+
}
55+
56+
RtlZeroMemory(buffer, bufferSize);
57+
RtlInitializeBitMap(&m_header, static_cast<PULONG>(buffer), size);
58+
59+
return STATUS_SUCCESS;
60+
}
61+
62+
ULONG size() const
63+
{
64+
return m_header.SizeOfBitMap;
65+
}
66+
67+
void setBits(ULONG startingIndex, ULONG numberToSet) noexcept
68+
{
69+
RtlSetBits(&m_header, startingIndex, numberToSet);
70+
}
71+
72+
void clearBits(ULONG startingIndex, ULONG numberToSet) noexcept
73+
{
74+
RtlClearBits(&m_header, startingIndex, numberToSet);
75+
}
76+
77+
void setAll() noexcept
78+
{
79+
RtlSetAllBits(&m_header);
80+
}
81+
82+
void clearAll() noexcept
83+
{
84+
RtlClearAllBits(&m_header);
85+
}
86+
87+
bool areBitsSet(ULONG startingIndex, ULONG size) noexcept
88+
{
89+
return RtlAreBitsSet(&m_header, startingIndex, size);
90+
}
91+
92+
bool areBitsClear(ULONG startingIndex, ULONG size) noexcept
93+
{
94+
return RtlAreBitsClear(&m_header, startingIndex, size);
95+
}
96+
97+
ULONG numberOfSetBits() noexcept
98+
{
99+
return RtlNumberOfSetBits(&m_header);
100+
}
101+
102+
ULONG numberOfClearBits() noexcept
103+
{
104+
return RtlNumberOfClearBits(&m_header);
105+
}
106+
107+
BitmapRangeIterator rangeIterator(ULONG startingIndex = 0) noexcept
108+
{
109+
return BitmapRangeIterator(&m_header, startingIndex);
110+
}
111+
112+
private:
113+
void deinitialize() noexcept
114+
{
115+
operator delete(m_header.Buffer);
116+
m_header = {};
117+
}
118+
119+
private:
120+
RTL_BITMAP m_header = {};
121+
};
122+
}

include/kf/BitmapRangeIterator.h

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
#pragma once
2+
#include <optional>
3+
#include <kf/stl/cassert>
4+
5+
namespace kf
6+
{
7+
//////////////////////////////////////////////////////////////////////////
8+
// BitmapRangeIterator - enumerates ranges of bits set to 1
9+
10+
class BitmapRangeIterator
11+
{
12+
public:
13+
BitmapRangeIterator(PRTL_BITMAP header, ULONG startingIndex = 0)
14+
: m_header(header)
15+
, m_index(startingIndex)
16+
{
17+
}
18+
19+
//
20+
// Return range of set bits as a pair { startIndex, lenght }, nullopt means the end of iteration
21+
//
22+
23+
std::optional<std::pair<ULONG, ULONG>> next()
24+
{
25+
const auto range = internalNext();
26+
27+
return range.second ? std::make_optional(range) : std::nullopt;
28+
}
29+
30+
private:
31+
std::pair<ULONG, ULONG> internalNext()
32+
{
33+
if (m_index >= m_header->SizeOfBitMap)
34+
{
35+
//
36+
// We're beyond the end, there is nothing to report
37+
//
38+
39+
return {};
40+
}
41+
42+
ULONG clearBitsStart = ULONG_MAX;
43+
ULONG clearBitsCount = RtlFindNextForwardRunClear(m_header, m_index, &clearBitsStart);
44+
45+
if (!clearBitsCount)
46+
{
47+
//
48+
// No clear bits are found, then we have the range at the end of the bitmap to report.
49+
// Set `clearBitsStart` beyond the last bit.
50+
//
51+
52+
clearBitsStart = m_header->SizeOfBitMap;
53+
}
54+
55+
//
56+
// Range of set bits is from the current index to the first clear bit
57+
//
58+
59+
std::pair<ULONG, ULONG> range = { m_index, clearBitsStart - m_index };
60+
61+
//
62+
// Update the current index to the next bit beyond the last clear bit we've found
63+
//
64+
65+
m_index = clearBitsStart + clearBitsCount;
66+
67+
if (!range.second)
68+
{
69+
//
70+
// We've got an empty range! The bitmap starts from clear bits so we need to run internalNext again
71+
//
72+
73+
assert(!range.first); // We expect this case only at the beginning of the bitmap
74+
75+
range = internalNext();
76+
}
77+
78+
return range;
79+
}
80+
81+
private:
82+
PRTL_BITMAP m_header;
83+
ULONG m_index = 0;
84+
};
85+
}

test/Bitmap.cpp

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#include "pch.h"
2+
#include <kf/Bitmap.h>
3+
4+
SCENARIO("Bitmap")
5+
{
6+
GIVEN("initialized bitmap with size 10")
7+
{
8+
kf::Bitmap<PagedPool> bitmap;
9+
REQUIRE_NT_SUCCESS(bitmap.initialize(10));
10+
11+
WHEN("do nothing")
12+
{
13+
THEN("bitmap size is 10")
14+
{
15+
REQUIRE(bitmap.size() == 10);
16+
}
17+
18+
THEN("each bit is clear")
19+
{
20+
for (int i = 0; i < 10; ++i)
21+
{
22+
REQUIRE(bitmap.areBitsClear(i, 1));
23+
}
24+
}
25+
26+
THEN("no bit is set")
27+
{
28+
for (int i = 0; i < 10; ++i)
29+
{
30+
REQUIRE(!bitmap.areBitsSet(i, 1));
31+
}
32+
}
33+
}
34+
}
35+
}

test/BitmapRangeIterator.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
#include "pch.h"
2+
#include <kf/Bitmap.h>
3+
4+
SCENARIO("BitmapRangeIterator")
5+
{
6+
GIVEN("initialized Bitmap with size 10")
7+
{
8+
kf::Bitmap<PagedPool> bitmap;
9+
REQUIRE_NT_SUCCESS(bitmap.initialize(10));
10+
11+
auto iterator = bitmap.rangeIterator();
12+
13+
WHEN("do nothing, bitmap is [0,0,0,0,0,0,0,0,0,0]")
14+
{
15+
THEN("iterator returs no ranges")
16+
{
17+
REQUIRE(!iterator.next());
18+
}
19+
}
20+
21+
WHEN("set bitmap to [1,0,0,0,0,0,0,0,0,0]")
22+
{
23+
bitmap.setBits(0, 1);
24+
25+
THEN("iterator returs ranges: {0,1}")
26+
{
27+
REQUIRE(*iterator.next() == std::make_pair(0UL, 1UL));
28+
REQUIRE(!iterator.next());
29+
}
30+
}
31+
32+
WHEN("set bitmap to [1,1,1,0,0,0,0,0,0,0]")
33+
{
34+
bitmap.setBits(0, 3);
35+
36+
THEN("iterator returs ranges: {0,3}")
37+
{
38+
REQUIRE(*iterator.next() == std::make_pair(0UL, 3UL));
39+
REQUIRE(!iterator.next());
40+
}
41+
}
42+
}
43+
}

test/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ wdk_add_driver(kf-test WINVER NTDDI_WIN10 STL
3939
pch.h
4040
pch.cpp
4141
AdjacentView.cpp
42+
Bitmap.cpp
43+
BitmapRangeIterator.cpp
4244
HexTest.cpp
4345
Vector.cpp
4446
)

0 commit comments

Comments
 (0)