/sparrowhawk/foundation/ESFBuddyAllocator.cpp
C++ | 415 lines | 238 code | 110 blank | 67 comment | 65 complexity | 6627a5cc6e53ab17492e7a6157c48384 MD5 | raw file
1/** @file ESFBuddyAllocator.cpp 2 * @brief A ESFAllocator implementation good for variable-length allocations 3 * 4 * Copyright (c) 2009 Yahoo! Inc. 5 * The copyrights embodied in the content of this file are licensed by Yahoo! Inc. 6 * under the BSD (revised) open source license. 7 * 8 * Derived from code that is Copyright (c) 2009 Joshua Blatt and offered under both 9 * BSD and Apache 2.0 licenses (http://sourceforge.net/projects/sparrowhawk/). 10 * 11 * $Author: blattj $ 12 * $Date: 2009/05/25 21:51:08 $ 13 * $Name: $ 14 * $Revision: 1.3 $ 15 */ 16 17#ifndef ESF_BUDDY_ALLOCATOR_H 18#include <ESFBuddyAllocator.h> 19#endif 20 21#ifndef ESF_ASSERT_H 22#include <ESFAssert.h> 23#endif 24 25ESFBuddyAllocator::KVal ESFBuddyAllocator::GetKVal(ESFUWord requestedSize) { 26 ESFUWord adjustedSize = requestedSize + sizeof(AvailListElem); 27 KVal kVal = 1; 28 29 while (adjustedSize > (ESF_UWORD_C(1) << kVal)) { 30 ++kVal; 31 } 32 33 return kVal; 34} 35 36ESFBuddyAllocator::AvailListElem *ESFBuddyAllocator::popAvailList(ESFBuddyAllocator::KVal kVal) { 37 ESF_ASSERT( kVal < ESF_AVAIL_LIST_LENGTH ); 38 39 AvailListElem *elem = _availList[kVal]; 40 41 if (!elem) 42 return 0; 43 44 AvailListElem *next = elem->_linkF; 45 46 ESF_ASSERT( 0 == elem->_linkB ); 47 ESF_ASSERT( kVal == elem->_kVal ); 48 ESF_ASSERT( 1 == elem->_tag ); 49 50 elem->_linkB = 0; 51 elem->_linkF = 0; 52 elem->_tag = 0; 53 54 if (next) { 55 next->_linkB = 0; 56 _availList[kVal] = next; 57 } else { 58 _availList[kVal] = 0; 59 } 60 61 return elem; 62} 63 64void ESFBuddyAllocator::removeFromAvailList(ESFBuddyAllocator::AvailListElem *elem) { 65 ESF_ASSERT( elem ); 66 ESF_ASSERT( elem->_kVal < ESF_AVAIL_LIST_LENGTH ); 67 68 if (elem->_linkB) { 69 elem->_linkB->_linkF = elem->_linkF; 70 } 71 72 if (elem->_linkF) { 73 elem->_linkF->_linkB = elem->_linkB; 74 } 75 76 if (!elem->_linkB) { 77 _availList[elem->_kVal] = elem->_linkF; 78 } 79 80 elem->_linkB = 0; 81 elem->_linkF = 0; 82 elem->_tag = 0; 83} 84 85void ESFBuddyAllocator::pushAvailList(AvailListElem *elem) { 86 ESF_ASSERT( elem ); 87 ESF_ASSERT( elem->_kVal < ESF_AVAIL_LIST_LENGTH ); 88 89 AvailListElem *next = _availList[elem->_kVal]; 90 91 elem->_linkB = 0; 92 elem->_linkF = next; 93 elem->_tag = 1; 94 95 if (next) { 96 next->_linkB = elem; 97 } 98 99 _availList[elem->_kVal] = elem; 100} 101 102ESFBuddyAllocator::ESFBuddyAllocator(int size, ESFAllocator *source) : 103 _failoverAllocator(0), _sourceAllocator(source), _pool(0), _poolKVal(0) { 104 if (1 > size || ESF_AVAIL_LIST_LENGTH < size) 105 return; 106 107 if (!_sourceAllocator) 108 return; 109 110 for (int i = 0; i < ESF_AVAIL_LIST_LENGTH; ++i) { 111 _availList[i] = 0; 112 } 113 114 _poolKVal = size; 115 _sourceAllocator = source; 116} 117 118ESFBuddyAllocator::~ESFBuddyAllocator() { 119 destroy(); 120} 121 122void * 123ESFBuddyAllocator::allocate(ESFUWord size) { 124 if (0 == size) { 125 return 0; 126 } 127 128 if (!_pool) { 129 if (ESF_SUCCESS != initialize()) { 130 return 0; 131 } 132 } 133 134 // 135 // Find an element large enough to fulfill the request. If found, remove 136 // it from the available list. If not found, try the failover allocator. 137 // 138 // Knuth R1 and R2 139 // 140 141 KVal minimumKVal = GetKVal(size); 142 KVal actualKVal = minimumKVal; 143 char *elem = 0; 144 145 for (; actualKVal < ESF_AVAIL_LIST_LENGTH; ++actualKVal) { 146 if (0 != _availList[actualKVal]) { 147 elem = (char *) popAvailList(actualKVal); 148 break; 149 } 150 } 151 152 if (!elem) { 153 if (!_failoverAllocator) 154 return 0; 155 156 return _failoverAllocator->allocate(size); 157 } 158 159 // 160 // Keep splitting the elem until we are left with a minimally sized elem. 161 // 162 // Knuth R3 and R4 163 // 164 165 ESF_ASSERT( actualKVal >= minimumKVal ); 166 167 AvailListElem *right = (AvailListElem *) elem; 168 AvailListElem *left = 0; 169 170 while (minimumKVal < actualKVal) { 171 --actualKVal; 172 173 left = (AvailListElem *) (elem + (ESF_UWORD_C(1) << actualKVal)); 174 175 right->_kVal = actualKVal; 176 177 left->_linkB = 0; 178 left->_linkF = 0; 179 left->_kVal = actualKVal; 180 left->_tag = 0; 181 182 pushAvailList(left); 183 } 184 185 ESF_ASSERT( right ); 186 187 return (void *) (((char *) right) + sizeof(AvailListElem)); 188} 189 190ESFError ESFBuddyAllocator::allocate(void **block, ESFUWord size) { 191 if (!block) { 192 return ESF_NULL_POINTER; 193 } 194 195 if (0 == size) { 196 return ESF_INVALID_ARGUMENT; 197 } 198 199 if (!_pool) { 200 ESFError error = initialize(); 201 202 if (ESF_SUCCESS != error) { 203 return error; 204 } 205 } 206 207 void *tmp = allocate(size); 208 209 if (!tmp) { 210 return ESF_OUT_OF_MEMORY; 211 } 212 213 *block = tmp; 214 215 return ESF_SUCCESS; 216} 217 218ESFError ESFBuddyAllocator::deallocate(void *block) { 219 if (!block) { 220 return ESF_NULL_POINTER; 221 } 222 223 if (!_pool) { 224 return ESF_INVALID_STATE; 225 } 226 227 char *trueAddress = ((char *) block) - sizeof(AvailListElem); 228 229 if (trueAddress < (char *) _pool || trueAddress >= (char *) _pool + (ESF_UWORD_C(1) << _poolKVal)) { 230 if (_failoverAllocator) { 231 return _failoverAllocator->deallocate(block); 232 } 233 234 return ESF_NOT_OWNER; 235 } 236 237 // 238 // Find this block's buddy and check its availability. 239 // 240 // Knuth S1 241 // 242 243 AvailListElem *elem = (AvailListElem *) trueAddress; 244 AvailListElem *buddy = 0; 245 246 ESF_ASSERT( 0 == elem->_linkB ); 247 ESF_ASSERT( 0 == elem->_linkF ); 248 ESF_ASSERT( 0 == elem->_tag ); 249 250 // 251 // For as long as we can, start coalescing buddies. Two buddies can be 252 // coalesced iff both are available and both are the same size (kVal). 253 // If they have different kVals, one of the buddies has been broken up 254 // into smaller nodes and cannot be coalesced until it is whole again. 255 // 256 // Knuth S2 and S3 257 // 258 259 while (true) { 260 // 261 // Base Case: We've coalesced everything back into the original block. 262 // 263 if (elem->_kVal == _poolKVal) { 264 ESF_ASSERT( ( char * ) elem == _pool ); 265 266 pushAvailList(elem); 267 268 break; 269 } 270 271 ESFUWord elemAddress = (ESFUWord) (((char *) elem) - (char *) _pool); 272 273 if (0 == elemAddress % (ESF_UWORD_C(1) << (elem->_kVal + 1))) { 274 // 275 // The buddy is to the right of this node. 276 // 277 278 buddy = (AvailListElem *) (((char *) elem) + (ESF_UWORD_C(1) << elem->_kVal)); 279 280 ESF_ASSERT( buddy->_kVal <= elem->_kVal ); 281 282 if (buddy->_tag && buddy->_kVal == elem->_kVal) { 283 removeFromAvailList(buddy); 284 285 // 286 // Coalesce two buddies. When coalescing two buddies, the 287 // address of the left buddy becomes the address of the new 288 // buddy. Also, the size of the new buddy doubles. 289 // 290 291 // elem = elem; 292 293 ++elem->_kVal; 294 295 continue; 296 } 297 298 // 299 // The buddy node isn't ready to be coalesced so just add 300 // the current node to the avail list. 301 // 302 303 pushAvailList(elem); 304 305 break; 306 } 307 308 // 309 // The buddy is to the left of this node. 310 // 311 312 buddy = (AvailListElem *) (((char *) elem) - (ESF_UWORD_C(1) << elem->_kVal)); 313 314 ESF_ASSERT( buddy->_kVal <= elem->_kVal ); 315 316 if (buddy->_tag && buddy->_kVal == elem->_kVal) { 317 removeFromAvailList(buddy); 318 319 // 320 // Coalesce two buddies. When coalescing two buddies, the 321 // address of the left buddy becomes the address of the new 322 // buddy. Also, the size of the new buddy doubles. 323 // 324 325 elem = buddy; 326 327 ++elem->_kVal; 328 329 continue; 330 } 331 332 // 333 // The buddy node isn't ready to be coalesced so just add 334 // the current node to the avail list. 335 // 336 337 pushAvailList(elem); 338 339 break; 340 } 341 342 return ESF_SUCCESS; 343} 344 345ESFUWord ESFBuddyAllocator::getOverhead() { 346 return sizeof(AvailListElem); 347} 348 349ESFError ESFBuddyAllocator::initialize() { 350 if (_pool || 1 > _poolKVal || ESF_AVAIL_LIST_LENGTH <= _poolKVal) { 351 return ESF_INVALID_STATE; 352 } 353 354 _pool = _sourceAllocator->allocate(ESF_UWORD_C(1) << _poolKVal); 355 356 if (0 == _pool) { 357 return ESF_OUT_OF_MEMORY; 358 } 359 360 ESF_ASSERT( _pool ); 361 362 AvailListElem *elem = (AvailListElem *) _pool; 363 364 elem->_linkB = 0; 365 elem->_linkF = 0; 366 elem->_tag = 1; 367 elem->_kVal = _poolKVal; 368 369 _availList[_poolKVal] = elem; 370 371 return ESF_SUCCESS; 372} 373 374ESFError ESFBuddyAllocator::destroy() { 375 if (!_pool) { 376 return ESF_INVALID_STATE; 377 } 378 379 if (0 == _availList[_poolKVal]) { 380 return ESF_IN_USE; 381 } 382 383 if (_failoverAllocator) { 384 ESFError error = _failoverAllocator->destroy(); 385 386 if (ESF_SUCCESS != error) { 387 return error; 388 } 389 } 390 391 _sourceAllocator->deallocate((void *) _pool); 392 _pool = 0; 393 394 return ESF_SUCCESS; 395} 396 397ESFError ESFBuddyAllocator::isInitialized() { 398 return _pool ? ESF_SUCCESS : ESF_NOT_INITIALIZED; 399} 400 401ESFError ESFBuddyAllocator::setFailoverAllocator(ESFAllocator *allocator) { 402 _failoverAllocator = allocator; 403 404 return ESF_SUCCESS; 405} 406 407ESFError ESFBuddyAllocator::getFailoverAllocator(ESFAllocator **allocator) { 408 if (!allocator) { 409 return ESF_NULL_POINTER; 410 } 411 412 *allocator = _failoverAllocator; 413 414 return ESF_SUCCESS; 415}