File indexing completed on 2025-10-20 08:27:34
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011 #ifndef BOOST_INTERPROCESS_DETAIL_MEM_ALGO_COMMON_HPP
0012 #define BOOST_INTERPROCESS_DETAIL_MEM_ALGO_COMMON_HPP
0013
0014 #ifndef BOOST_CONFIG_HPP
0015 # include <boost/config.hpp>
0016 #endif
0017 #
0018 #if defined(BOOST_HAS_PRAGMA_ONCE)
0019 # pragma once
0020 #endif
0021
0022 #include <boost/interprocess/detail/config_begin.hpp>
0023 #include <boost/interprocess/detail/workaround.hpp>
0024
0025
0026 #include <boost/interprocess/interprocess_fwd.hpp>
0027 #include <boost/interprocess/containers/allocation_type.hpp>
0028
0029 #include <boost/interprocess/detail/math_functions.hpp>
0030 #include <boost/interprocess/detail/min_max.hpp>
0031 #include <boost/interprocess/detail/type_traits.hpp>
0032 #include <boost/interprocess/detail/utilities.hpp>
0033
0034 #include <boost/container/detail/multiallocation_chain.hpp>
0035 #include <boost/container/detail/placement_new.hpp>
0036
0037 #include <boost/move/utility_core.hpp>
0038
0039 #include <boost/move/detail/force_ptr.hpp>
0040
0041 #include <boost/assert.hpp>
0042
0043
0044
0045
0046 namespace boost {
0047 namespace interprocess {
0048 namespace ipcdetail {
0049
0050 template<class VoidPointer>
0051 class basic_multiallocation_chain
0052 : public boost::container::dtl::
0053 basic_multiallocation_chain<VoidPointer>
0054 {
0055 BOOST_MOVABLE_BUT_NOT_COPYABLE(basic_multiallocation_chain)
0056 typedef boost::container::dtl::
0057 basic_multiallocation_chain<VoidPointer> base_t;
0058 public:
0059
0060 basic_multiallocation_chain()
0061 : base_t()
0062 {}
0063
0064 basic_multiallocation_chain(BOOST_RV_REF(basic_multiallocation_chain) other)
0065 : base_t(::boost::move(static_cast<base_t&>(other)))
0066 {}
0067
0068 basic_multiallocation_chain& operator=(BOOST_RV_REF(basic_multiallocation_chain) other)
0069 {
0070 this->base_t::operator=(::boost::move(static_cast<base_t&>(other)));
0071 return *this;
0072 }
0073
0074 void *pop_front()
0075 {
0076 return boost::interprocess::ipcdetail::to_raw_pointer(this->base_t::pop_front());
0077 }
0078 };
0079
0080
0081
0082 template<class MemoryAlgorithm>
0083 class memory_algorithm_common
0084 {
0085 public:
0086 typedef typename MemoryAlgorithm::void_pointer void_pointer;
0087 typedef typename MemoryAlgorithm::block_ctrl block_ctrl;
0088 typedef typename MemoryAlgorithm::multiallocation_chain multiallocation_chain;
0089 typedef memory_algorithm_common<MemoryAlgorithm> this_type;
0090 typedef typename MemoryAlgorithm::size_type size_type;
0091
0092 static const size_type Alignment = MemoryAlgorithm::Alignment;
0093 static const size_type AllocatedCtrlBytes = MemoryAlgorithm::AllocatedCtrlBytes;
0094 static const size_type AllocatedCtrlUnits = MemoryAlgorithm::AllocatedCtrlUnits;
0095 static const size_type BlockCtrlBytes = MemoryAlgorithm::BlockCtrlBytes;
0096 static const size_type BlockCtrlUnits = MemoryAlgorithm::BlockCtrlUnits;
0097 static const size_type UsableByPreviousChunk = MemoryAlgorithm::UsableByPreviousChunk;
0098
0099 static void assert_alignment(const void *ptr)
0100 { assert_alignment((std::size_t)ptr); }
0101
0102 static void assert_alignment(size_type uint_ptr)
0103 {
0104 (void)uint_ptr;
0105 BOOST_ASSERT(uint_ptr % Alignment == 0);
0106 }
0107
0108 static bool check_alignment(const void *ptr)
0109 { return (((std::size_t)ptr) % Alignment == 0); }
0110
0111 static size_type ceil_units(size_type size)
0112 { return get_rounded_size(size, Alignment)/Alignment; }
0113
0114 static size_type floor_units(size_type size)
0115 { return size/Alignment; }
0116
0117 static size_type user_buffer_ceil_units(size_type size)
0118 {
0119 if(size <= UsableByPreviousChunk)
0120 return 0;
0121 return ceil_units(size - UsableByPreviousChunk);
0122 }
0123
0124 static size_type multiple_of_units(size_type size)
0125 { return get_rounded_size(size, Alignment); }
0126
0127 static void allocate_many
0128 (MemoryAlgorithm *memory_algo, size_type elem_bytes, size_type n_elements, multiallocation_chain &chain)
0129 {
0130 return this_type::priv_allocate_many(memory_algo, &elem_bytes, n_elements, 0, chain);
0131 }
0132
0133 static void deallocate_many(MemoryAlgorithm *memory_algo, multiallocation_chain &chain)
0134 {
0135 return this_type::priv_deallocate_many(memory_algo, chain);
0136 }
0137
0138 static bool calculate_lcm_and_needs_backwards_lcmed
0139 (size_type backwards_multiple, size_type received_size, size_type size_to_achieve,
0140 size_type &lcm_out, size_type &needs_backwards_lcmed_out)
0141 {
0142
0143 size_type max = backwards_multiple;
0144 size_type min = Alignment;
0145 size_type needs_backwards;
0146 size_type needs_backwards_lcmed;
0147 size_type lcm_val;
0148 size_type current_forward;
0149
0150 if(max < min){
0151 size_type tmp = min;
0152 min = max;
0153 max = tmp;
0154 }
0155
0156 if((backwards_multiple & (backwards_multiple-1)) == 0){
0157 if(0 != (size_to_achieve & ((backwards_multiple-1)))){
0158 return false;
0159 }
0160
0161 lcm_val = max;
0162
0163
0164
0165 current_forward = get_truncated_size_po2(received_size, backwards_multiple);
0166 needs_backwards = size_to_achieve - current_forward;
0167 BOOST_ASSERT((needs_backwards % backwards_multiple) == 0);
0168 needs_backwards_lcmed = get_rounded_size_po2(needs_backwards, lcm_val);
0169 lcm_out = lcm_val;
0170 needs_backwards_lcmed_out = needs_backwards_lcmed;
0171 return true;
0172 }
0173
0174 else if((backwards_multiple & (Alignment - 1u)) == 0){
0175 lcm_val = backwards_multiple;
0176 current_forward = get_truncated_size(received_size, backwards_multiple);
0177
0178 needs_backwards_lcmed = needs_backwards = size_to_achieve - current_forward;
0179 BOOST_ASSERT((needs_backwards_lcmed & (Alignment - 1u)) == 0);
0180 lcm_out = lcm_val;
0181 needs_backwards_lcmed_out = needs_backwards_lcmed;
0182 return true;
0183 }
0184
0185 else if((backwards_multiple & ((Alignment/2u) - 1u)) == 0){
0186 lcm_val = backwards_multiple*2u;
0187 current_forward = get_truncated_size(received_size, backwards_multiple);
0188 needs_backwards_lcmed = needs_backwards = size_to_achieve - current_forward;
0189 if(0 != (needs_backwards_lcmed & (Alignment-1)))
0190
0191 needs_backwards_lcmed += backwards_multiple;
0192 BOOST_ASSERT((needs_backwards_lcmed % lcm_val) == 0);
0193 lcm_out = lcm_val;
0194 needs_backwards_lcmed_out = needs_backwards_lcmed;
0195 return true;
0196 }
0197
0198 else if((backwards_multiple & ((Alignment/4u) - 1u)) == 0){
0199 size_type remainder;
0200 lcm_val = backwards_multiple*4u;
0201 current_forward = get_truncated_size(received_size, backwards_multiple);
0202 needs_backwards_lcmed = needs_backwards = size_to_achieve - current_forward;
0203
0204
0205 if(0 != (remainder = ((needs_backwards_lcmed & (Alignment-1))>>(Alignment/8u)))){
0206 if(backwards_multiple & Alignment/2u){
0207 needs_backwards_lcmed += (remainder)*backwards_multiple;
0208 }
0209 else{
0210 needs_backwards_lcmed += (4-remainder)*backwards_multiple;
0211 }
0212 }
0213 BOOST_ASSERT((needs_backwards_lcmed % lcm_val) == 0);
0214 lcm_out = lcm_val;
0215 needs_backwards_lcmed_out = needs_backwards_lcmed;
0216 return true;
0217 }
0218 else{
0219 lcm_val = lcm(max, min);
0220 }
0221
0222
0223
0224 current_forward = get_truncated_size(received_size, backwards_multiple);
0225 needs_backwards = size_to_achieve - current_forward;
0226 BOOST_ASSERT((needs_backwards % backwards_multiple) == 0);
0227 needs_backwards_lcmed = get_rounded_size(needs_backwards, lcm_val);
0228 lcm_out = lcm_val;
0229 needs_backwards_lcmed_out = needs_backwards_lcmed;
0230 return true;
0231 }
0232
0233 static void allocate_many
0234 ( MemoryAlgorithm *memory_algo
0235 , const size_type *elem_sizes
0236 , size_type n_elements
0237 , size_type sizeof_element
0238 , multiallocation_chain &chain)
0239 {
0240 this_type::priv_allocate_many(memory_algo, elem_sizes, n_elements, sizeof_element, chain);
0241 }
0242
0243 static void* allocate_aligned
0244 (MemoryAlgorithm * const memory_algo, const size_type nbytes, const size_type alignment)
0245 {
0246
0247
0248 const bool alignment_ok = (alignment & (alignment - 1u)) == 0;
0249 if (!alignment_ok){
0250
0251 BOOST_ASSERT(alignment_ok);
0252 return 0;
0253 }
0254
0255 if(alignment <= Alignment){
0256 size_type real_size = nbytes;
0257 void *ignore_reuse = 0;
0258 return memory_algo->priv_allocate
0259 (boost::interprocess::allocate_new, nbytes, real_size, ignore_reuse);
0260 }
0261
0262
0263 size_type needed_units = user_buffer_ceil_units(nbytes);
0264
0265
0266
0267 needed_units += max_value(needed_units, BlockCtrlUnits - AllocatedCtrlUnits);
0268
0269
0270 needed_units += BlockCtrlUnits;
0271
0272 needed_units += (alignment - Alignment)/Alignment;
0273
0274
0275 const size_type request = needed_units*Alignment + UsableByPreviousChunk;
0276
0277
0278 size_type real_size = request;
0279 void *ignore_reuse = 0;
0280 void *const buffer = memory_algo->priv_allocate(boost::interprocess::allocate_new, request, real_size, ignore_reuse);
0281 if(!buffer){
0282 return 0;
0283 }
0284 else if ((((std::size_t)(buffer)) & (alignment-1)) == 0){
0285
0286
0287 block_ctrl *const first = memory_algo->priv_get_block(buffer);
0288 const size_type orig_first_units = first->m_size;
0289 const size_type first_min_units =
0290 max_value(user_buffer_ceil_units(nbytes) + AllocatedCtrlUnits, size_type(BlockCtrlUnits));
0291
0292 if(orig_first_units >= (first_min_units + BlockCtrlUnits)){
0293 block_ctrl *second = move_detail::force_ptr<block_ctrl*>
0294 (reinterpret_cast<char*>(first) + Alignment*first_min_units);
0295
0296 first->m_size = first_min_units & block_ctrl::size_mask;
0297 memory_algo->priv_mark_new_allocated_block(first);
0298
0299
0300 second->m_size = (orig_first_units - first_min_units) & block_ctrl::size_mask;
0301 memory_algo->priv_mark_new_allocated_block(second);
0302 memory_algo->priv_deallocate(memory_algo->priv_get_user_buffer(second));
0303 }
0304 return buffer;
0305 }
0306
0307
0308 block_ctrl* const first = memory_algo->priv_get_block(buffer);
0309
0310 BOOST_ASSERT(memory_algo->priv_is_allocated_block(first));
0311
0312 BOOST_ASSERT(first->m_size >= (needed_units + AllocatedCtrlUnits));
0313
0314 BOOST_ASSERT(first->m_size >= 2 * BlockCtrlUnits);
0315
0316
0317
0318
0319
0320
0321
0322
0323
0324
0325
0326
0327
0328 char *const usr_buf = reinterpret_cast<char*>
0329 (reinterpret_cast<std::size_t>(static_cast<char*>(buffer)
0330 + BlockCtrlBytes
0331 + alignment - 1) & -alignment);
0332
0333
0334 BOOST_ASSERT(usr_buf <= (reinterpret_cast<char*>(first) + first->m_size*Alignment));
0335
0336 BOOST_ASSERT((usr_buf + nbytes) <= (reinterpret_cast<char*>(first) + first->m_size*Alignment + UsableByPreviousChunk));
0337
0338
0339 const size_type orig_first_units = first->m_size;
0340
0341 block_ctrl* const second = memory_algo->priv_get_block(usr_buf);
0342
0343
0344 const size_type final_first_units =
0345 size_type(reinterpret_cast<char*>(second) - reinterpret_cast<char*>(first))/Alignment & block_ctrl::size_mask;
0346
0347
0348
0349
0350
0351
0352
0353
0354
0355
0356
0357
0358 const size_type orig_second_units = orig_first_units - final_first_units;
0359 const size_type second_min_units = max_value( size_type(BlockCtrlUnits)
0360 , user_buffer_ceil_units(nbytes) + AllocatedCtrlUnits );
0361
0362
0363 if(orig_second_units >= (second_min_units + BlockCtrlUnits)){
0364
0365 block_ctrl *const third = ::new (reinterpret_cast<char*>(second) + Alignment*second_min_units, boost_container_new_t()) block_ctrl;
0366 second->m_size = second_min_units & block_ctrl::size_mask;
0367 third->m_size = (orig_second_units - second->m_size) & block_ctrl::size_mask;
0368 BOOST_ASSERT(third->m_size >= BlockCtrlUnits);
0369 memory_algo->priv_mark_new_allocated_block(second);
0370 memory_algo->priv_mark_new_allocated_block(third);
0371
0372 memory_algo->priv_deallocate(memory_algo->priv_get_user_buffer(third));
0373 }
0374 else{
0375 second->m_size = orig_second_units & block_ctrl::size_mask;
0376 BOOST_ASSERT(second->m_size >= BlockCtrlUnits);
0377 memory_algo->priv_mark_new_allocated_block(second);
0378 }
0379
0380
0381 first->m_size = final_first_units & block_ctrl::size_mask;
0382
0383 memory_algo->priv_mark_new_allocated_block(first);
0384 memory_algo->priv_deallocate(memory_algo->priv_get_user_buffer(first));
0385
0386
0387 BOOST_ASSERT((reinterpret_cast<char*>(usr_buf) + nbytes) <= (reinterpret_cast<char*>(second) + second->m_size*Alignment + UsableByPreviousChunk));
0388
0389 BOOST_ASSERT(0 == ((std::size_t)usr_buf & (alignment-1u)));
0390 return usr_buf;
0391 }
0392
0393 static bool try_shrink
0394 (MemoryAlgorithm *memory_algo, void *ptr
0395 ,const size_type max_size, size_type &received_size)
0396 {
0397 size_type const preferred_size = received_size;
0398 (void)memory_algo;
0399
0400 block_ctrl *block = memory_algo->priv_get_block(ptr);
0401 size_type old_block_units = (size_type)block->m_size;
0402
0403
0404 BOOST_ASSERT(memory_algo->priv_is_allocated_block(block));
0405
0406
0407 assert_alignment(ptr);
0408
0409
0410 received_size = (old_block_units - AllocatedCtrlUnits)*Alignment + UsableByPreviousChunk;
0411
0412
0413 const size_type max_user_units = floor_units(max_size - UsableByPreviousChunk);
0414 const size_type preferred_user_units = ceil_units(preferred_size - UsableByPreviousChunk);
0415
0416
0417 if(max_user_units < preferred_user_units)
0418 return false;
0419
0420
0421 size_type old_user_units = old_block_units - AllocatedCtrlUnits;
0422
0423 if(old_user_units < preferred_user_units)
0424 return false;
0425
0426
0427 if(old_user_units == preferred_user_units)
0428 return true;
0429
0430 size_type shrunk_user_units =
0431 ((BlockCtrlUnits - AllocatedCtrlUnits) >= preferred_user_units)
0432 ? (BlockCtrlUnits - AllocatedCtrlUnits)
0433 : preferred_user_units;
0434
0435
0436 if(max_user_units < shrunk_user_units)
0437 return false;
0438
0439
0440 if((old_user_units - shrunk_user_units) < BlockCtrlUnits ){
0441 return false;
0442 }
0443
0444
0445 received_size = shrunk_user_units*Alignment + UsableByPreviousChunk;
0446 return true;
0447 }
0448
0449 static bool shrink
0450 (MemoryAlgorithm *memory_algo, void *ptr
0451 ,const size_type max_size, size_type &received_size)
0452 {
0453 size_type const preferred_size = received_size;
0454
0455 block_ctrl *block = memory_algo->priv_get_block(ptr);
0456 size_type old_block_units = (size_type)block->m_size;
0457
0458 if(!try_shrink(memory_algo, ptr, max_size, received_size)){
0459 return false;
0460 }
0461
0462
0463 if((old_block_units - AllocatedCtrlUnits) == ceil_units(preferred_size - UsableByPreviousChunk))
0464 return true;
0465
0466
0467 block->m_size = ((received_size-UsableByPreviousChunk)/Alignment + AllocatedCtrlUnits) & block_ctrl::size_mask;
0468 BOOST_ASSERT(block->m_size >= BlockCtrlUnits);
0469
0470
0471 block_ctrl *new_block = move_detail::force_ptr<block_ctrl*>
0472 (reinterpret_cast<char*>(block) + block->m_size*Alignment);
0473
0474
0475 new_block->m_size = (old_block_units - block->m_size) & block_ctrl::size_mask;
0476 BOOST_ASSERT(new_block->m_size >= BlockCtrlUnits);
0477 memory_algo->priv_mark_new_allocated_block(block);
0478 memory_algo->priv_mark_new_allocated_block(new_block);
0479 memory_algo->priv_deallocate(memory_algo->priv_get_user_buffer(new_block));
0480 return true;
0481 }
0482
0483 private:
0484 static void priv_allocate_many
0485 ( MemoryAlgorithm *memory_algo
0486 , const size_type *elem_sizes
0487 , size_type n_elements
0488 , size_type sizeof_element
0489 , multiallocation_chain &chain)
0490 {
0491
0492
0493
0494
0495 size_type total_request_units = 0;
0496 size_type elem_units = 0;
0497 const size_type ptr_size_units = memory_algo->priv_get_total_units(sizeof(void_pointer));
0498 if(!sizeof_element){
0499 elem_units = memory_algo->priv_get_total_units(*elem_sizes);
0500 elem_units = ptr_size_units > elem_units ? ptr_size_units : elem_units;
0501 total_request_units = n_elements*elem_units;
0502 }
0503 else{
0504 for(size_type i = 0; i < n_elements; ++i){
0505 if(multiplication_overflows(elem_sizes[i], sizeof_element)){
0506 total_request_units = 0;
0507 break;
0508 }
0509 elem_units = memory_algo->priv_get_total_units(elem_sizes[i]*sizeof_element);
0510 elem_units = ptr_size_units > elem_units ? ptr_size_units : elem_units;
0511 if(sum_overflows(total_request_units, elem_units)){
0512 total_request_units = 0;
0513 break;
0514 }
0515 total_request_units += elem_units;
0516 }
0517 }
0518
0519 if(total_request_units && !multiplication_overflows(total_request_units, Alignment)){
0520 size_type low_idx = 0;
0521 while(low_idx < n_elements){
0522 size_type total_bytes = total_request_units*Alignment - AllocatedCtrlBytes + UsableByPreviousChunk;
0523 size_type min_allocation = (!sizeof_element)
0524 ? elem_units
0525 : memory_algo->priv_get_total_units(elem_sizes[low_idx]*sizeof_element);
0526 min_allocation = min_allocation*Alignment - AllocatedCtrlBytes + UsableByPreviousChunk;
0527
0528 size_type received_size = total_bytes;
0529 void *ignore_reuse = 0;
0530 void *ret = memory_algo->priv_allocate
0531 (boost::interprocess::allocate_new, min_allocation, received_size, ignore_reuse);
0532 if(!ret){
0533 break;
0534 }
0535
0536 block_ctrl *block = memory_algo->priv_get_block(ret);
0537 size_type received_units = (size_type)block->m_size;
0538 char *block_address = reinterpret_cast<char*>(block);
0539
0540 size_type total_used_units = 0;
0541 while(total_used_units < received_units){
0542 if(sizeof_element){
0543 elem_units = memory_algo->priv_get_total_units(elem_sizes[low_idx]*sizeof_element);
0544 elem_units = ptr_size_units > elem_units ? ptr_size_units : elem_units;
0545 }
0546 if(total_used_units + elem_units > received_units)
0547 break;
0548 total_request_units -= elem_units;
0549
0550 block_ctrl *new_block = move_detail::force_ptr<block_ctrl*>(block_address);
0551 assert_alignment(new_block);
0552
0553
0554 if((low_idx + 1) == n_elements ||
0555 (total_used_units + elem_units +
0556 ((!sizeof_element)
0557 ? elem_units
0558 : max_value(memory_algo->priv_get_total_units(elem_sizes[low_idx+1]*sizeof_element), ptr_size_units))
0559 > received_units)){
0560
0561 new_block->m_size = (received_units - total_used_units) & block_ctrl::size_mask;
0562 memory_algo->priv_mark_new_allocated_block(new_block);
0563
0564
0565
0566 if((received_units - total_used_units) >= (elem_units + MemoryAlgorithm::BlockCtrlUnits)){
0567 size_type shrunk_request = elem_units*Alignment - AllocatedCtrlBytes + UsableByPreviousChunk;
0568 size_type shrunk_received = shrunk_request;
0569 bool shrink_ok = shrink
0570 (memory_algo
0571 ,memory_algo->priv_get_user_buffer(new_block)
0572 ,shrunk_request
0573 ,shrunk_received);
0574 (void)shrink_ok;
0575
0576 BOOST_ASSERT(shrink_ok);
0577
0578 BOOST_ASSERT(shrunk_request == shrunk_received);
0579 BOOST_ASSERT(elem_units == ((shrunk_request-UsableByPreviousChunk)/Alignment + AllocatedCtrlUnits));
0580
0581 BOOST_ASSERT(new_block->m_size == elem_units);
0582
0583 received_units = elem_units + total_used_units;
0584 }
0585 }
0586 else{
0587 new_block->m_size = elem_units & block_ctrl::size_mask;
0588 memory_algo->priv_mark_new_allocated_block(new_block);
0589 }
0590
0591 block_address += new_block->m_size*Alignment;
0592 total_used_units += (size_type)new_block->m_size;
0593
0594 BOOST_ASSERT((new_block->m_size*Alignment - AllocatedCtrlUnits) >= sizeof(void_pointer));
0595 void_pointer p = ::new(memory_algo->priv_get_user_buffer(new_block), boost_container_new_t())void_pointer(0);
0596 chain.push_back(p);
0597 ++low_idx;
0598 }
0599
0600 BOOST_ASSERT(total_used_units == received_units);
0601 }
0602
0603 if(low_idx != n_elements){
0604 priv_deallocate_many(memory_algo, chain);
0605 }
0606 }
0607 }
0608
0609 static void priv_deallocate_many(MemoryAlgorithm *memory_algo, multiallocation_chain &chain)
0610 {
0611 while(!chain.empty()){
0612 memory_algo->priv_deallocate(to_raw_pointer(chain.pop_front()));
0613 }
0614 }
0615 };
0616
0617 }
0618 }
0619 }
0620
0621 #include <boost/interprocess/detail/config_end.hpp>
0622
0623 #endif