forked from stepan-dolgorukov/allocator
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathallocator.cxx
More file actions
146 lines (112 loc) · 4.08 KB
/
allocator.cxx
File metadata and controls
146 lines (112 loc) · 4.08 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
/*!
\file allocator.cxx
\brief В файле определяется класс allocator.
*/
#include <algorithm>
#include <cstddef>
#include <mutex>
#include <stdexcept>
#include <sys/mman.h>
#include "./allocator.hxx"
/*!
\brief Создать аллокатор.
\param size_block Размер блока.
\param blocks_amount Количество выделяемых блоков.
*/
allocator::allocator( std::size_t size_block, std::size_t blocks_amount )
: _size_block{ size_block }, _blocks_amount{ blocks_amount } {
if ( _size_block == 0u ) {
throw std::invalid_argument{ "A block size must be greater than 0." };
}
if ( _blocks_amount == 0u ) {
throw std::invalid_argument{ "An amount of blocks must be greater than 0." };
}
_size_storage = _size_block * _blocks_amount;
/* Проверка на переполнение: произведение _size_block * _blocks_amount может превышать SIZE_MAX. Произведение
n-битных чисел есть (2n)-битное число. */
if ( _size_block != ( _size_storage / _blocks_amount ) || _blocks_amount != ( _size_storage / _size_block ) ) {
throw std::overflow_error{ "A production size_block * blocks_amount must be "
"no greater than SIZE_MAX." };
}
_storage = reinterpret_cast< std::byte * >(
mmap( nullptr, _size_storage, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0 ) );
if ( _storage == nullptr ) {
throw std::bad_alloc{};
}
_pointer = _storage;
}
allocator::~allocator() { munmap( _storage, _size_storage ); }
/*!
\brief Выделить указанное число блоков памяти.
\param blocks_amount Запрашиваемое число блоков.
*/
auto allocator::take( std::size_t blocks_amount ) -> decltype( _storage ) {
std::lock_guard lock{ _mutex };
if ( blocks_amount == 0u ) {
return nullptr;
}
if ( blocks_amount > _blocks_amount ) {
return nullptr;
}
const std::ptrdiff_t size{ _pointer - _storage };
const auto amount_bytes_to_allocate{ blocks_amount * _size_block };
if ( size + amount_bytes_to_allocate > _size_storage ) {
_pointer = _storage;
}
while ( _pointer + amount_bytes_to_allocate < _storage + _size_storage ) {
const auto find_result{ _left_borders.find( _pointer ) };
if ( find_result != _left_borders.end() ) {
_pointer += find_result.operator*().second * _size_block;
}
else {
std::byte * left_border{};
for ( const auto & [address, length] : _left_borders ) {
if ( address > _pointer ) {
if ( left_border == nullptr ) {
left_border = address;
}
else {
left_border = min( address, left_border );
}
}
}
if ( left_border != nullptr ) {
const std::ptrdiff_t length{ left_border - _pointer };
if ( length < amount_bytes_to_allocate ) {
_pointer = left_border + _left_borders.at( left_border );
continue;
}
}
break;
}
}
if ( _pointer + amount_bytes_to_allocate > _storage + _size_storage ) {
_pointer = _storage;
return nullptr;
}
if ( _left_borders.find( _pointer ) != _left_borders.end() ) {
_pointer = _storage;
return nullptr;
}
auto pointer{ _pointer };
_left_borders.emplace( pointer, blocks_amount );
if ( _pointer + amount_bytes_to_allocate == _storage + _size_storage ) {
_pointer = _storage;
}
else {
_pointer += amount_bytes_to_allocate;
}
return pointer;
}
/*!
\brief Освободить выделенный участок памяти.
\param block_pointer Указатель на левую границу ранее выделенного блока.
*/
void allocator::release( std::byte * block_pointer ) {
std::lock_guard lock{ _mutex };
const auto result_find{ _left_borders.find( block_pointer ) };
if ( result_find == _left_borders.end() ) {
throw std::invalid_argument{ "Address doesn't match a left border of a block." };
}
_left_borders.erase( result_find );
}