Skip to content

Commit 04ad6bb

Browse files
belyshevdenisSerhiiMalyiElwooD07CopilotDenys Bielyshev
authored
Add wrapper for std::map and tests for it (#15)
* Add wrapper for std::map * Fixes after review * add kmtest submodule * add cmake build configuration * make kf as static library * fix build with STL features * implement tests for Hex, fix compilation and linkage * fix _Xout_of_range logic Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * fix misspelling in Hex.h Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * fix parameters check in kf::Hex::decode * fix tests build and linkage * implement tests for Map * implement tests for Map * add kfbenchmark, make benchmark for kf::Map * Update kfbenchmark/pch.cpp Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update test/MapTest.cpp Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update include/kf/Hex.h Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * move GetTickCount to the usage space * move GetTickCount to the usage space * make kf project as header-only target * replace gitmodules with FetchContent * rename kfbenchmark to benchmark, fix its build * remove debug message * split generation of standalone project by CMake from simple inclusion to another CMake project * update README.md and add it to the project root * refactoring * Fix build with kf headers included * fix MapTest and MapBenchmark * fix build in Release mode * move common CXX settings to the main CMakeLists.txt * fix kf/stl/new, remove extra operators * revert hex and test * remove kf-benchmark * revert CMakeLists.txt for kf.test * fix new operator * delete CMakeLists.txt for include * changes for review * changes for review * move BuildHelpers out of main kf project * fixes for review * add more noexcept for EarlyAllocator * fixes for review * fixes for review * delete _Xbad_alloc implementation * fix _Raise_handler_impl * Clean up and fix optional access * Rename and move map to stl directory --------- Co-authored-by: Serhii Malyi <maliy.sergey@apriorit.com> Co-authored-by: Denys Bielyshev <elwood0777@gmail.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Denys Bielyshev <belyshevdenis@apriorit.com> Co-authored-by: Sergey Podobry <sergius@apriorit.com>
1 parent 43b5bc8 commit 04ad6bb

File tree

9 files changed

+438
-13
lines changed

9 files changed

+438
-13
lines changed

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,4 @@ if(${IS_TOPLEVEL_PROJECT})
4949
endif()
5050
endif()
5151

52-
# TODO: add install target
52+
# TODO: add install target

include/kf/EarlyAllocator.h

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,22 +27,24 @@ namespace kf
2727
{
2828
}
2929

30-
_CONSTEXPR20_DYNALLOC ~EarlyAllocator() = default;
31-
_CONSTEXPR20_DYNALLOC EarlyAllocator& operator=(const EarlyAllocator&) = default;
30+
~EarlyAllocator() noexcept = default;
31+
EarlyAllocator& operator=(const EarlyAllocator&) noexcept = default;
3232

3333
template<POOL_TYPE poolType>
34-
T* initialize(const size_t count)
34+
T* initialize(const size_t count) noexcept
3535
{
36-
assert(!m_ptr);
37-
assert(!m_size);
36+
if (m_ptr || m_size)
37+
{
38+
_Xinvalid_argument("m_ptr || m_size");
39+
}
3840

3941
m_size = count * sizeof(T);
4042
m_ptr = static_cast<T*>(operator new(m_size, poolType));
4143

4244
return m_ptr;
4345
}
4446

45-
_CONSTEXPR20_DYNALLOC void deallocate(const T* ptr, const size_t count)
47+
void deallocate(const T* ptr, const size_t count) noexcept
4648
{
4749
if (ptr != m_ptr || count * sizeof(T) > m_size)
4850
{
@@ -54,7 +56,7 @@ namespace kf
5456
m_size = 0;
5557
}
5658

57-
_NODISCARD _CONSTEXPR20_DYNALLOC T* allocate(const size_t count)
59+
_NODISCARD T* allocate(const size_t count) noexcept
5860
{
5961
if (!m_ptr || count * sizeof(T) > m_size)
6062
{

include/kf/MapAllocator.h

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
#pragma once
2+
#include <kf/stl/memory>
3+
4+
namespace kf
5+
{
6+
// TODO: refactor
7+
8+
struct AllocatorState
9+
{
10+
std::unique_ptr<char[]> buffer;
11+
size_t size;
12+
};
13+
14+
template<typename T, POOL_TYPE poolType>
15+
class MapAllocator
16+
{
17+
public:
18+
using value_type = T;
19+
20+
MapAllocator() = default;
21+
22+
// Needed because allocator has extra parameter for POOL_TYPE
23+
template <typename U>
24+
struct rebind
25+
{
26+
using other = MapAllocator<U, poolType>;
27+
};
28+
29+
friend class MapAllocator;
30+
31+
template<typename U>
32+
MapAllocator(const MapAllocator<U, poolType>& other)
33+
: m_state(other.m_state)
34+
{
35+
}
36+
37+
[[nodiscard]] bool initialize()
38+
{
39+
m_state = make_shared<AllocatorState, poolType>();
40+
return m_state != nullptr;
41+
}
42+
43+
T* allocate(std::size_t n)
44+
{
45+
if (!m_state || !m_state->buffer)
46+
{
47+
return nullptr;
48+
}
49+
50+
if (m_state->size < n)
51+
{
52+
return nullptr;
53+
}
54+
55+
auto buffer = reinterpret_cast<T*>(m_state->buffer.release());
56+
m_state->size = 0;
57+
return buffer;
58+
}
59+
60+
void deallocate(T*, std::size_t)
61+
{
62+
m_state->buffer.reset();
63+
m_state->size = 0;
64+
}
65+
66+
[[nodiscard]] bool prepareMemory(std::size_t size)
67+
{
68+
if (!m_state)
69+
{
70+
return false;
71+
}
72+
73+
if (m_state->buffer)
74+
{
75+
return true;
76+
}
77+
78+
m_state->buffer.reset(new(poolType) char[size]);
79+
if (!m_state->buffer)
80+
{
81+
return false;
82+
}
83+
m_state->size = size;
84+
85+
return true;
86+
}
87+
88+
private:
89+
std::shared_ptr<AllocatorState> m_state;
90+
};
91+
}

include/kf/UString.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ namespace kf
7575

7676
if (newByteLength > 0)
7777
{
78+
#pragma warning(suppress : 4996) // ExAllocatePoolWithTag is deprecated, use ExAllocatePool2
7879
#pragma warning(suppress: 28160) // Must succeed pool allocations are forbidden. Allocation failures cause a system crash.
7980
newBuffer = ::ExAllocatePoolWithTag(poolType, newByteLength, PoolTag);
8081

include/kf/stl/map

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
#pragma once
2+
#include <kf/MapAllocator.h>
3+
#include <map>
4+
#include <optional>
5+
6+
#if _ITERATOR_DEBUG_LEVEL > 0
7+
#error "_ITERATOR_DEBUG_LEVEL must not be greater than 0"
8+
#endif
9+
10+
namespace kf
11+
{
12+
//////////////////////////////////////////////////////////////////////////
13+
// map - wraps std::map and allows using it in exception-free environments.
14+
//
15+
// Note: some methods return NTSTATUS or std::optional to indicate an error
16+
// thus they differ from std::map!
17+
18+
template<typename KeyType, typename ValueType, POOL_TYPE poolType, typename LessComparer = std::less<KeyType>>
19+
class map
20+
{
21+
public:
22+
using allocator_type = MapAllocator<std::pair<const KeyType, ValueType>, poolType>;
23+
using map_type = std::map<KeyType, ValueType, LessComparer, allocator_type>;
24+
using key_type = map_type::key_type;
25+
using mapped_type = map_type::mapped_type;
26+
using value_type = map_type::value_type;
27+
using size_type = map_type::size_type;
28+
using iterator = map_type::iterator;
29+
using const_iterator = map_type::const_iterator;
30+
31+
public:
32+
map() = default;
33+
34+
map(const map&) = delete;
35+
map& operator=(const map&) = delete;
36+
37+
map(map&& other) = default;
38+
map& operator=(map&& other) = default;
39+
40+
[[nodiscard]] NTSTATUS initialize()
41+
{
42+
allocator_type allocator;
43+
if (!allocator.initialize())
44+
{
45+
return STATUS_INSUFFICIENT_RESOURCES;
46+
}
47+
48+
// Prepare memory for head node
49+
if (!allocator.prepareMemory(kNodeSize))
50+
{
51+
return STATUS_INSUFFICIENT_RESOURCES;
52+
}
53+
54+
m_internalMap = make_unique<map_type, poolType>(allocator);
55+
if (!m_internalMap)
56+
{
57+
return STATUS_INSUFFICIENT_RESOURCES;
58+
}
59+
60+
return STATUS_SUCCESS;
61+
}
62+
63+
std::optional<std::reference_wrapper<ValueType>> operator[](KeyType&& key)
64+
{
65+
if (!m_internalMap->get_allocator().prepareMemory(kNodeSize))
66+
{
67+
return {};
68+
}
69+
70+
return std::ref((*m_internalMap)[std::forward<KeyType>(key)]);
71+
}
72+
73+
template <class... Args>
74+
std::optional<std::pair<iterator, bool>> emplace(Args&&... values)
75+
{
76+
if (!m_internalMap->get_allocator().prepareMemory(kNodeSize))
77+
{
78+
return {};
79+
}
80+
81+
return m_internalMap->emplace(std::forward<Args>(values)...);
82+
}
83+
84+
[[nodiscard]] iterator find(const KeyType& key)
85+
{
86+
return m_internalMap->find(key);
87+
}
88+
89+
[[nodiscard]] const_iterator find(const KeyType& key) const
90+
{
91+
return m_internalMap->find(key);
92+
}
93+
94+
[[nodiscard]] bool contains(const KeyType& key) const
95+
{
96+
return m_internalMap->contains(key);
97+
}
98+
99+
void clear() noexcept
100+
{
101+
m_internalMap->clear();
102+
}
103+
104+
iterator erase(const_iterator where) noexcept
105+
{
106+
return m_internalMap->erase(where);
107+
}
108+
109+
iterator erase(const_iterator first, const_iterator last) noexcept
110+
{
111+
return m_internalMap->erase(first, last);
112+
}
113+
114+
size_type erase(const KeyType& key) noexcept
115+
{
116+
return m_internalMap->erase(key);
117+
}
118+
119+
[[nodiscard]] iterator begin() noexcept
120+
{
121+
return m_internalMap->begin();
122+
}
123+
124+
[[nodiscard]] const_iterator begin() const noexcept
125+
{
126+
return m_internalMap->begin();
127+
}
128+
129+
[[nodiscard]] iterator end() noexcept
130+
{
131+
return m_internalMap->end();
132+
}
133+
134+
[[nodiscard]] const_iterator end() const noexcept
135+
{
136+
return m_internalMap->end();
137+
}
138+
139+
[[nodiscard]] iterator upper_bound(const KeyType& key)
140+
{
141+
return m_internalMap->upper_bound(key);
142+
}
143+
144+
[[nodiscard]] const_iterator upper_bound(const KeyType& key) const
145+
{
146+
return m_internalMap->upper_bound(key);
147+
}
148+
149+
[[nodiscard]] iterator lower_bound(const KeyType& key)
150+
{
151+
return m_internalMap->lower_bound(key);
152+
}
153+
154+
[[nodiscard]] const_iterator lower_bound(const KeyType& key) const
155+
{
156+
return m_internalMap->lower_bound(key);
157+
}
158+
159+
bool empty() const noexcept
160+
{
161+
return m_internalMap->empty();
162+
}
163+
164+
size_type size() const noexcept
165+
{
166+
return m_internalMap->size();
167+
}
168+
169+
private:
170+
static constexpr size_t kNodeSize = sizeof(map_type::_Alnode_traits::value_type);
171+
std::unique_ptr<map_type> m_internalMap;
172+
};
173+
}

include/kf/stl/memory

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#pragma once
22
#include <memory>
33
#include <type_traits>
4+
#include <kf/stl/new>
45
#include <kf/EarlyAllocator.h>
56

67
template<typename T, POOL_TYPE PoolType, typename... TArgs>

include/kf/stl/new

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,23 @@
22

33
#pragma warning(push)
44
#pragma warning(disable : 4595) // non-member operator new or delete functions may not be declared inline
5-
#pragma warning(disable : 4996) // TODO: ExAllocatePoolWithTag is deprecated, use ExAllocatePool2
65

7-
inline void* __cdecl operator new(size_t size, POOL_TYPE poolType)
6+
inline void* __cdecl operator new(size_t size, POOL_TYPE poolType) noexcept
87
{
98
#pragma warning(suppress: 28160) // Must succeed pool allocations are forbidden. Allocation failures cause a system crash.
9+
#pragma warning(suppress: 4996) // ExAllocatePoolWithTag is deprecated, use ExAllocatePool2
1010
return ::ExAllocatePoolWithTag(poolType, size ? size : 1, 'n++C');
1111
}
1212

13-
inline void __cdecl operator delete(void* ptr)
13+
inline void __cdecl operator delete(void* ptr) noexcept
1414
{
1515
if (ptr)
1616
{
1717
::ExFreePoolWithTag(ptr, 'n++C');
1818
}
1919
}
2020

21-
inline void __cdecl operator delete[](void* ptr)
21+
inline void __cdecl operator delete[](void* ptr) noexcept
2222
{
2323
if (ptr)
2424
{
@@ -27,7 +27,14 @@ inline void __cdecl operator delete[](void* ptr)
2727
}
2828

2929
// size-aware deallocation
30-
inline void __cdecl operator delete(void* ptr, size_t)
30+
inline void __cdecl operator delete(void* ptr, size_t) noexcept
31+
{
32+
operator delete(ptr);
33+
}
34+
35+
// To avoid warning C4291: 'void *operator new(size_t,POOL_TYPE) noexcept':
36+
// no matching operator delete found; memory will not be freed if initialization throws an exception
37+
inline void operator delete(void* ptr, POOL_TYPE) noexcept
3138
{
3239
operator delete(ptr);
3340
}

test/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ wdk_add_driver(kf-test WINVER NTDDI_WIN10 STL
4242
Bitmap.cpp
4343
BitmapRangeIterator.cpp
4444
HexTest.cpp
45+
MapTest.cpp
4546
Vector.cpp
4647
)
4748

0 commit comments

Comments
 (0)