cmemory.h (30667B)
1 // © 2016 and later: Unicode, Inc. and others. 2 // License & terms of use: http://www.unicode.org/copyright.html 3 /* 4 ****************************************************************************** 5 * 6 * Copyright (C) 1997-2016, International Business Machines 7 * Corporation and others. All Rights Reserved. 8 * 9 ****************************************************************************** 10 * 11 * File CMEMORY.H 12 * 13 * Contains stdlib.h/string.h memory functions 14 * 15 * @author Bertrand A. Damiba 16 * 17 * Modification History: 18 * 19 * Date Name Description 20 * 6/20/98 Bertrand Created. 21 * 05/03/99 stephen Changed from functions to macros. 22 * 23 ****************************************************************************** 24 */ 25 26 #ifndef CMEMORY_H 27 #define CMEMORY_H 28 29 #include "unicode/utypes.h" 30 31 #include <stddef.h> 32 #include <string.h> 33 #include "unicode/localpointer.h" 34 #include "uassert.h" 35 36 #if U_DEBUG && defined(UPRV_MALLOC_COUNT) 37 #include <stdio.h> 38 #endif 39 40 // uprv_memcpy and uprv_memmove 41 #if defined(__clang__) 42 #define uprv_memcpy(dst, src, size) UPRV_BLOCK_MACRO_BEGIN { \ 43 /* Suppress warnings about addresses that will never be NULL */ \ 44 _Pragma("clang diagnostic push") \ 45 _Pragma("clang diagnostic ignored \"-Waddress\"") \ 46 U_ASSERT(dst != NULL); \ 47 U_ASSERT(src != NULL); \ 48 _Pragma("clang diagnostic pop") \ 49 U_STANDARD_CPP_NAMESPACE memcpy(dst, src, size); \ 50 } UPRV_BLOCK_MACRO_END 51 #define uprv_memmove(dst, src, size) UPRV_BLOCK_MACRO_BEGIN { \ 52 /* Suppress warnings about addresses that will never be NULL */ \ 53 _Pragma("clang diagnostic push") \ 54 _Pragma("clang diagnostic ignored \"-Waddress\"") \ 55 U_ASSERT(dst != NULL); \ 56 U_ASSERT(src != NULL); \ 57 _Pragma("clang diagnostic pop") \ 58 U_STANDARD_CPP_NAMESPACE memmove(dst, src, size); \ 59 } UPRV_BLOCK_MACRO_END 60 #elif defined(__GNUC__) 61 #define uprv_memcpy(dst, src, size) UPRV_BLOCK_MACRO_BEGIN { \ 62 /* Suppress warnings about addresses that will never be NULL */ \ 63 _Pragma("GCC diagnostic push") \ 64 _Pragma("GCC diagnostic ignored \"-Waddress\"") \ 65 U_ASSERT(dst != NULL); \ 66 U_ASSERT(src != NULL); \ 67 _Pragma("GCC diagnostic pop") \ 68 U_STANDARD_CPP_NAMESPACE memcpy(dst, src, size); \ 69 } UPRV_BLOCK_MACRO_END 70 #define uprv_memmove(dst, src, size) UPRV_BLOCK_MACRO_BEGIN { \ 71 /* Suppress warnings about addresses that will never be NULL */ \ 72 _Pragma("GCC diagnostic push") \ 73 _Pragma("GCC diagnostic ignored \"-Waddress\"") \ 74 U_ASSERT(dst != NULL); \ 75 U_ASSERT(src != NULL); \ 76 _Pragma("GCC diagnostic pop") \ 77 U_STANDARD_CPP_NAMESPACE memmove(dst, src, size); \ 78 } UPRV_BLOCK_MACRO_END 79 #else 80 #define uprv_memcpy(dst, src, size) UPRV_BLOCK_MACRO_BEGIN { \ 81 U_ASSERT(dst != NULL); \ 82 U_ASSERT(src != NULL); \ 83 U_STANDARD_CPP_NAMESPACE memcpy(dst, src, size); \ 84 } UPRV_BLOCK_MACRO_END 85 #define uprv_memmove(dst, src, size) UPRV_BLOCK_MACRO_BEGIN { \ 86 U_ASSERT(dst != NULL); \ 87 U_ASSERT(src != NULL); \ 88 U_STANDARD_CPP_NAMESPACE memmove(dst, src, size); \ 89 } UPRV_BLOCK_MACRO_END 90 #endif 91 92 /** 93 * \def UPRV_LENGTHOF 94 * Convenience macro to determine the length of a fixed array at compile-time. 95 * @param array A fixed length array 96 * @return The length of the array, in elements 97 * @internal 98 */ 99 #define UPRV_LENGTHOF(array) (int32_t)(sizeof(array)/sizeof((array)[0])) 100 #define uprv_memset(buffer, mark, size) U_STANDARD_CPP_NAMESPACE memset(buffer, mark, size) 101 #define uprv_memcmp(buffer1, buffer2, size) U_STANDARD_CPP_NAMESPACE memcmp(buffer1, buffer2,size) 102 #define uprv_memchr(ptr, value, num) U_STANDARD_CPP_NAMESPACE memchr(ptr, value, num) 103 104 U_CAPI void * U_EXPORT2 105 uprv_malloc(size_t s) U_MALLOC_ATTR U_ALLOC_SIZE_ATTR(1); 106 107 U_CAPI void * U_EXPORT2 108 uprv_realloc(void *mem, size_t size) U_ALLOC_SIZE_ATTR(2); 109 110 U_CAPI void U_EXPORT2 111 uprv_free(void *mem); 112 113 U_CAPI void * U_EXPORT2 114 uprv_calloc(size_t num, size_t size) U_MALLOC_ATTR U_ALLOC_SIZE_ATTR2(1,2); 115 116 /** 117 * Get the least significant bits of a pointer (a memory address). 118 * For example, with a mask of 3, the macro gets the 2 least significant bits, 119 * which will be 0 if the pointer is 32-bit (4-byte) aligned. 120 * 121 * uintptr_t is the most appropriate integer type to cast to. 122 */ 123 #define U_POINTER_MASK_LSB(ptr, mask) ((uintptr_t)(ptr) & (mask)) 124 125 /** 126 * Create & return an instance of "type" in statically allocated storage. 127 * e.g. 128 * static std::mutex *myMutex = STATIC_NEW(std::mutex); 129 * To destroy an object created in this way, invoke the destructor explicitly, e.g. 130 * myMutex->~mutex(); 131 * DO NOT use delete. 132 * DO NOT use with class UMutex, which has specific support for static instances. 133 * 134 * STATIC_NEW is intended for use when 135 * - We want a static (or global) object. 136 * - We don't want it to ever be destructed, or to explicitly control destruction, 137 * to avoid use-after-destruction problems. 138 * - We want to avoid an ordinary heap allocated object, 139 * to avoid the possibility of memory allocation failures, and 140 * to avoid memory leak reports, from valgrind, for example. 141 * This is defined as a macro rather than a template function because each invocation 142 * must define distinct static storage for the object being returned. 143 */ 144 #define STATIC_NEW(type) [] () { \ 145 alignas(type) static char storage[sizeof(type)]; \ 146 return new(storage) type();} () 147 148 /** 149 * Heap clean up function, called from u_cleanup() 150 * Clears any user heap functions from u_setMemoryFunctions() 151 * Does NOT deallocate any remaining allocated memory. 152 */ 153 U_CFUNC UBool 154 cmemory_cleanup(void); 155 156 /** 157 * A function called by <TT>uhash_remove</TT>, 158 * <TT>uhash_close</TT>, or <TT>uhash_put</TT> to delete 159 * an existing key or value. 160 * @param obj A key or value stored in a hashtable 161 * @see uprv_deleteUObject 162 */ 163 typedef void U_CALLCONV UObjectDeleter(void* obj); 164 165 /** 166 * Deleter for UObject instances. 167 * Works for all subclasses of UObject because it has a virtual destructor. 168 */ 169 U_CAPI void U_EXPORT2 170 uprv_deleteUObject(void *obj); 171 172 #ifdef __cplusplus 173 174 #include <utility> 175 #include "unicode/uobject.h" 176 177 U_NAMESPACE_BEGIN 178 179 /** 180 * "Smart pointer" class, deletes memory via uprv_free(). 181 * For most methods see the LocalPointerBase base class. 182 * Adds operator[] for array item access. 183 * 184 * @see LocalPointerBase 185 */ 186 template<typename T> 187 class LocalMemory : public LocalPointerBase<T> { 188 public: 189 using LocalPointerBase<T>::operator*; 190 using LocalPointerBase<T>::operator->; 191 /** 192 * Constructor takes ownership. 193 * @param p simple pointer to an array of T items that is adopted 194 */ 195 explicit LocalMemory(T *p=nullptr) : LocalPointerBase<T>(p) {} 196 /** 197 * Move constructor, leaves src with isNull(). 198 * @param src source smart pointer 199 */ 200 LocalMemory(LocalMemory<T> &&src) noexcept : LocalPointerBase<T>(src.ptr) { 201 src.ptr=nullptr; 202 } 203 /** 204 * Destructor deletes the memory it owns. 205 */ 206 ~LocalMemory() { 207 uprv_free(LocalPointerBase<T>::ptr); 208 } 209 /** 210 * Move assignment operator, leaves src with isNull(). 211 * The behavior is undefined if *this and src are the same object. 212 * @param src source smart pointer 213 * @return *this 214 */ 215 LocalMemory<T> &operator=(LocalMemory<T> &&src) noexcept { 216 uprv_free(LocalPointerBase<T>::ptr); 217 LocalPointerBase<T>::ptr=src.ptr; 218 src.ptr=nullptr; 219 return *this; 220 } 221 /** 222 * Swap pointers. 223 * @param other other smart pointer 224 */ 225 void swap(LocalMemory<T> &other) noexcept { 226 T *temp=LocalPointerBase<T>::ptr; 227 LocalPointerBase<T>::ptr=other.ptr; 228 other.ptr=temp; 229 } 230 /** 231 * Non-member LocalMemory swap function. 232 * @param p1 will get p2's pointer 233 * @param p2 will get p1's pointer 234 */ 235 friend inline void swap(LocalMemory<T> &p1, LocalMemory<T> &p2) noexcept { 236 p1.swap(p2); 237 } 238 /** 239 * Deletes the array it owns, 240 * and adopts (takes ownership of) the one passed in. 241 * @param p simple pointer to an array of T items that is adopted 242 */ 243 void adoptInstead(T *p) { 244 uprv_free(LocalPointerBase<T>::ptr); 245 LocalPointerBase<T>::ptr=p; 246 } 247 /** 248 * Deletes the array it owns, allocates a new one and reset its bytes to 0. 249 * Returns the new array pointer. 250 * If the allocation fails, then the current array is unchanged and 251 * this method returns nullptr. 252 * @param newCapacity must be >0 253 * @return the allocated array pointer, or nullptr if the allocation failed 254 */ 255 inline T *allocateInsteadAndReset(int32_t newCapacity=1); 256 /** 257 * Deletes the array it owns and allocates a new one, copying length T items. 258 * Returns the new array pointer. 259 * If the allocation fails, then the current array is unchanged and 260 * this method returns nullptr. 261 * @param newCapacity must be >0 262 * @param length number of T items to be copied from the old array to the new one; 263 * must be no more than the capacity of the old array, 264 * which the caller must track because the LocalMemory does not track it 265 * @return the allocated array pointer, or nullptr if the allocation failed 266 */ 267 inline T *allocateInsteadAndCopy(int32_t newCapacity=1, int32_t length=0); 268 /** 269 * Array item access (writable). 270 * No index bounds check. 271 * @param i array index 272 * @return reference to the array item 273 */ 274 T &operator[](ptrdiff_t i) const { return LocalPointerBase<T>::ptr[i]; } 275 }; 276 277 template<typename T> 278 inline T *LocalMemory<T>::allocateInsteadAndReset(int32_t newCapacity) { 279 if(newCapacity>0) { 280 T *p=(T *)uprv_malloc(newCapacity*sizeof(T)); 281 if(p!=nullptr) { 282 uprv_memset(p, 0, newCapacity*sizeof(T)); 283 uprv_free(LocalPointerBase<T>::ptr); 284 LocalPointerBase<T>::ptr=p; 285 } 286 return p; 287 } else { 288 return nullptr; 289 } 290 } 291 292 293 template<typename T> 294 inline T *LocalMemory<T>::allocateInsteadAndCopy(int32_t newCapacity, int32_t length) { 295 if(newCapacity>0) { 296 T *p=(T *)uprv_malloc(newCapacity*sizeof(T)); 297 if(p!=nullptr) { 298 if(length>0) { 299 if(length>newCapacity) { 300 length=newCapacity; 301 } 302 uprv_memcpy(p, LocalPointerBase<T>::ptr, (size_t)length*sizeof(T)); 303 } 304 uprv_free(LocalPointerBase<T>::ptr); 305 LocalPointerBase<T>::ptr=p; 306 } 307 return p; 308 } else { 309 return nullptr; 310 } 311 } 312 313 /** 314 * Simple array/buffer management class using uprv_malloc() and uprv_free(). 315 * Provides an internal array with fixed capacity. Can alias another array 316 * or allocate one. 317 * 318 * The array address is properly aligned for type T. It might not be properly 319 * aligned for types larger than T (or larger than the largest subtype of T). 320 * 321 * Unlike LocalMemory and LocalArray, this class never adopts 322 * (takes ownership of) another array. 323 * 324 * WARNING: MaybeStackArray only works with primitive (plain-old data) types. 325 * It does NOT know how to call a destructor! If you work with classes with 326 * destructors, consider: 327 * 328 * - LocalArray in localpointer.h if you know the length ahead of time 329 * - MaybeStackVector if you know the length at runtime 330 */ 331 template<typename T, int32_t stackCapacity> 332 class MaybeStackArray { 333 public: 334 // No heap allocation. Use only on the stack. 335 static void* U_EXPORT2 operator new(size_t) noexcept = delete; 336 static void* U_EXPORT2 operator new[](size_t) noexcept = delete; 337 static void* U_EXPORT2 operator new(size_t, void*) noexcept = delete; 338 339 /** 340 * Default constructor initializes with internal T[stackCapacity] buffer. 341 */ 342 MaybeStackArray() : ptr(stackArray), capacity(stackCapacity), needToRelease(false) {} 343 /** 344 * Automatically allocates the heap array if the argument is larger than the stack capacity. 345 * Intended for use when an approximate capacity is known at compile time but the true 346 * capacity is not known until runtime. 347 */ 348 MaybeStackArray(int32_t newCapacity, UErrorCode status) : MaybeStackArray() { 349 if (U_FAILURE(status)) { 350 return; 351 } 352 if (capacity < newCapacity) { 353 if (resize(newCapacity) == nullptr) { 354 status = U_MEMORY_ALLOCATION_ERROR; 355 } 356 } 357 } 358 /** 359 * Destructor deletes the array (if owned). 360 */ 361 ~MaybeStackArray() { releaseArray(); } 362 /** 363 * Move constructor: transfers ownership or copies the stack array. 364 */ 365 MaybeStackArray(MaybeStackArray<T, stackCapacity> &&src) noexcept; 366 /** 367 * Move assignment: transfers ownership or copies the stack array. 368 */ 369 MaybeStackArray<T, stackCapacity> &operator=(MaybeStackArray<T, stackCapacity> &&src) noexcept; 370 /** 371 * Returns the array capacity (number of T items). 372 * @return array capacity 373 */ 374 int32_t getCapacity() const { return capacity; } 375 /** 376 * Access without ownership change. 377 * @return the array pointer 378 */ 379 T *getAlias() const { return ptr; } 380 /** 381 * Returns the array limit. Simple convenience method. 382 * @return getAlias()+getCapacity() 383 */ 384 T *getArrayLimit() const { return getAlias()+capacity; } 385 // No "operator T *() const" because that can make 386 // expressions like mbs[index] ambiguous for some compilers. 387 /** 388 * Array item access (const). 389 * No index bounds check. 390 * @param i array index 391 * @return reference to the array item 392 */ 393 const T &operator[](ptrdiff_t i) const { return ptr[i]; } 394 /** 395 * Array item access (writable). 396 * No index bounds check. 397 * @param i array index 398 * @return reference to the array item 399 */ 400 T &operator[](ptrdiff_t i) { return ptr[i]; } 401 /** 402 * Deletes the array (if owned) and aliases another one, no transfer of ownership. 403 * If the arguments are illegal, then the current array is unchanged. 404 * @param otherArray must not be nullptr 405 * @param otherCapacity must be >0 406 */ 407 void aliasInstead(T *otherArray, int32_t otherCapacity) { 408 if(otherArray!=nullptr && otherCapacity>0) { 409 releaseArray(); 410 ptr=otherArray; 411 capacity=otherCapacity; 412 needToRelease=false; 413 } 414 } 415 /** 416 * Deletes the array (if owned) and allocates a new one, copying length T items. 417 * Returns the new array pointer. 418 * If the allocation fails, then the current array is unchanged and 419 * this method returns nullptr. 420 * @param newCapacity can be less than or greater than the current capacity; 421 * must be >0 422 * @param length number of T items to be copied from the old array to the new one 423 * @return the allocated array pointer, or nullptr if the allocation failed 424 */ 425 inline T *resize(int32_t newCapacity, int32_t length=0); 426 /** 427 * Gives up ownership of the array if owned, or else clones it, 428 * copying length T items; resets itself to the internal stack array. 429 * Returns nullptr if the allocation failed. 430 * @param length number of T items to copy when cloning, 431 * and capacity of the clone when cloning 432 * @param resultCapacity will be set to the returned array's capacity (output-only) 433 * @return the array pointer; 434 * caller becomes responsible for deleting the array 435 */ 436 inline T *orphanOrClone(int32_t length, int32_t &resultCapacity); 437 438 protected: 439 // Resizes the array to the size of src, then copies the contents of src. 440 void copyFrom(const MaybeStackArray &src, UErrorCode &status) { 441 if (U_FAILURE(status)) { 442 return; 443 } 444 if (this->resize(src.capacity, 0) == nullptr) { 445 status = U_MEMORY_ALLOCATION_ERROR; 446 return; 447 } 448 uprv_memcpy(this->ptr, src.ptr, (size_t)capacity * sizeof(T)); 449 } 450 451 private: 452 T *ptr; 453 int32_t capacity; 454 UBool needToRelease; 455 T stackArray[stackCapacity]; 456 void releaseArray() { 457 if(needToRelease) { 458 uprv_free(ptr); 459 } 460 } 461 void resetToStackArray() { 462 ptr=stackArray; 463 capacity=stackCapacity; 464 needToRelease=false; 465 } 466 /* No comparison operators with other MaybeStackArray's. */ 467 bool operator==(const MaybeStackArray & /*other*/) = delete; 468 bool operator!=(const MaybeStackArray & /*other*/) = delete; 469 /* No ownership transfer: No copy constructor, no assignment operator. */ 470 MaybeStackArray(const MaybeStackArray & /*other*/) = delete; 471 void operator=(const MaybeStackArray & /*other*/) = delete; 472 }; 473 474 template<typename T, int32_t stackCapacity> 475 icu::MaybeStackArray<T, stackCapacity>::MaybeStackArray( 476 MaybeStackArray <T, stackCapacity>&& src) noexcept 477 : ptr(src.ptr), capacity(src.capacity), needToRelease(src.needToRelease) { 478 if (src.ptr == src.stackArray) { 479 ptr = stackArray; 480 uprv_memcpy(stackArray, src.stackArray, sizeof(T) * src.capacity); 481 } else { 482 src.resetToStackArray(); // take ownership away from src 483 } 484 } 485 486 template<typename T, int32_t stackCapacity> 487 inline MaybeStackArray <T, stackCapacity>& 488 MaybeStackArray<T, stackCapacity>::operator=(MaybeStackArray <T, stackCapacity>&& src) noexcept { 489 releaseArray(); // in case this instance had its own memory allocated 490 capacity = src.capacity; 491 needToRelease = src.needToRelease; 492 if (src.ptr == src.stackArray) { 493 ptr = stackArray; 494 uprv_memcpy(stackArray, src.stackArray, sizeof(T) * src.capacity); 495 } else { 496 ptr = src.ptr; 497 src.resetToStackArray(); // take ownership away from src 498 } 499 return *this; 500 } 501 502 template<typename T, int32_t stackCapacity> 503 inline T *MaybeStackArray<T, stackCapacity>::resize(int32_t newCapacity, int32_t length) { 504 if(newCapacity>0) { 505 #if U_DEBUG && defined(UPRV_MALLOC_COUNT) 506 ::fprintf(::stderr, "MaybeStackArray (resize) alloc %d * %lu\n", newCapacity, sizeof(T)); 507 #endif 508 T *p=(T *)uprv_malloc(newCapacity*sizeof(T)); 509 if(p!=nullptr) { 510 if(length>0) { 511 if(length>capacity) { 512 length=capacity; 513 } 514 if(length>newCapacity) { 515 length=newCapacity; 516 } 517 uprv_memcpy(p, ptr, (size_t)length*sizeof(T)); 518 } 519 releaseArray(); 520 ptr=p; 521 capacity=newCapacity; 522 needToRelease=true; 523 } 524 return p; 525 } else { 526 return nullptr; 527 } 528 } 529 530 template<typename T, int32_t stackCapacity> 531 inline T *MaybeStackArray<T, stackCapacity>::orphanOrClone(int32_t length, int32_t &resultCapacity) { 532 T *p; 533 if(needToRelease) { 534 p=ptr; 535 } else if(length<=0) { 536 return nullptr; 537 } else { 538 if(length>capacity) { 539 length=capacity; 540 } 541 p=(T *)uprv_malloc(length*sizeof(T)); 542 #if U_DEBUG && defined(UPRV_MALLOC_COUNT) 543 ::fprintf(::stderr,"MaybeStacArray (orphan) alloc %d * %lu\n", length,sizeof(T)); 544 #endif 545 if(p==nullptr) { 546 return nullptr; 547 } 548 uprv_memcpy(p, ptr, (size_t)length*sizeof(T)); 549 } 550 resultCapacity=length; 551 resetToStackArray(); 552 return p; 553 } 554 555 /** 556 * Variant of MaybeStackArray that allocates a header struct and an array 557 * in one contiguous memory block, using uprv_malloc() and uprv_free(). 558 * Provides internal memory with fixed array capacity. Can alias another memory 559 * block or allocate one. 560 * The stackCapacity is the number of T items in the internal memory, 561 * not counting the H header. 562 * Unlike LocalMemory and LocalArray, this class never adopts 563 * (takes ownership of) another memory block. 564 */ 565 template<typename H, typename T, int32_t stackCapacity> 566 class MaybeStackHeaderAndArray { 567 public: 568 // No heap allocation. Use only on the stack. 569 static void* U_EXPORT2 operator new(size_t) noexcept = delete; 570 static void* U_EXPORT2 operator new[](size_t) noexcept = delete; 571 static void* U_EXPORT2 operator new(size_t, void*) noexcept = delete; 572 573 /** 574 * Default constructor initializes with internal H+T[stackCapacity] buffer. 575 */ 576 MaybeStackHeaderAndArray() : ptr(&stackHeader), capacity(stackCapacity), needToRelease(false) {} 577 /** 578 * Destructor deletes the memory (if owned). 579 */ 580 ~MaybeStackHeaderAndArray() { releaseMemory(); } 581 /** 582 * Returns the array capacity (number of T items). 583 * @return array capacity 584 */ 585 int32_t getCapacity() const { return capacity; } 586 /** 587 * Access without ownership change. 588 * @return the header pointer 589 */ 590 H *getAlias() const { return ptr; } 591 /** 592 * Returns the array start. 593 * @return array start, same address as getAlias()+1 594 */ 595 T *getArrayStart() const { return reinterpret_cast<T *>(getAlias()+1); } 596 /** 597 * Returns the array limit. 598 * @return array limit 599 */ 600 T *getArrayLimit() const { return getArrayStart()+capacity; } 601 /** 602 * Access without ownership change. Same as getAlias(). 603 * A class instance can be used directly in expressions that take a T *. 604 * @return the header pointer 605 */ 606 operator H *() const { return ptr; } 607 /** 608 * Array item access (writable). 609 * No index bounds check. 610 * @param i array index 611 * @return reference to the array item 612 */ 613 T &operator[](ptrdiff_t i) { return getArrayStart()[i]; } 614 /** 615 * Deletes the memory block (if owned) and aliases another one, no transfer of ownership. 616 * If the arguments are illegal, then the current memory is unchanged. 617 * @param otherArray must not be nullptr 618 * @param otherCapacity must be >0 619 */ 620 void aliasInstead(H *otherMemory, int32_t otherCapacity) { 621 if(otherMemory!=nullptr && otherCapacity>0) { 622 releaseMemory(); 623 ptr=otherMemory; 624 capacity=otherCapacity; 625 needToRelease=false; 626 } 627 } 628 /** 629 * Deletes the memory block (if owned) and allocates a new one, 630 * copying the header and length T array items. 631 * Returns the new header pointer. 632 * If the allocation fails, then the current memory is unchanged and 633 * this method returns nullptr. 634 * @param newCapacity can be less than or greater than the current capacity; 635 * must be >0 636 * @param length number of T items to be copied from the old array to the new one 637 * @return the allocated pointer, or nullptr if the allocation failed 638 */ 639 inline H *resize(int32_t newCapacity, int32_t length=0); 640 /** 641 * Gives up ownership of the memory if owned, or else clones it, 642 * copying the header and length T array items; resets itself to the internal memory. 643 * Returns nullptr if the allocation failed. 644 * @param length number of T items to copy when cloning, 645 * and array capacity of the clone when cloning 646 * @param resultCapacity will be set to the returned array's capacity (output-only) 647 * @return the header pointer; 648 * caller becomes responsible for deleting the array 649 */ 650 inline H *orphanOrClone(int32_t length, int32_t &resultCapacity); 651 private: 652 H *ptr; 653 int32_t capacity; 654 UBool needToRelease; 655 // stackHeader must precede stackArray immediately. 656 H stackHeader; 657 T stackArray[stackCapacity]; 658 void releaseMemory() { 659 if(needToRelease) { 660 uprv_free(ptr); 661 } 662 } 663 /* No comparison operators with other MaybeStackHeaderAndArray's. */ 664 bool operator==(const MaybeStackHeaderAndArray & /*other*/) {return false;} 665 bool operator!=(const MaybeStackHeaderAndArray & /*other*/) {return true;} 666 /* No ownership transfer: No copy constructor, no assignment operator. */ 667 MaybeStackHeaderAndArray(const MaybeStackHeaderAndArray & /*other*/) {} 668 void operator=(const MaybeStackHeaderAndArray & /*other*/) {} 669 }; 670 671 template<typename H, typename T, int32_t stackCapacity> 672 inline H *MaybeStackHeaderAndArray<H, T, stackCapacity>::resize(int32_t newCapacity, 673 int32_t length) { 674 if(newCapacity>=0) { 675 #if U_DEBUG && defined(UPRV_MALLOC_COUNT) 676 ::fprintf(::stderr,"MaybeStackHeaderAndArray alloc %d + %d * %ul\n", sizeof(H),newCapacity,sizeof(T)); 677 #endif 678 H *p=(H *)uprv_malloc(sizeof(H)+newCapacity*sizeof(T)); 679 if(p!=nullptr) { 680 if(length<0) { 681 length=0; 682 } else if(length>0) { 683 if(length>capacity) { 684 length=capacity; 685 } 686 if(length>newCapacity) { 687 length=newCapacity; 688 } 689 } 690 uprv_memcpy(p, ptr, sizeof(H)+(size_t)length*sizeof(T)); 691 releaseMemory(); 692 ptr=p; 693 capacity=newCapacity; 694 needToRelease=true; 695 } 696 return p; 697 } else { 698 return nullptr; 699 } 700 } 701 702 template<typename H, typename T, int32_t stackCapacity> 703 inline H *MaybeStackHeaderAndArray<H, T, stackCapacity>::orphanOrClone(int32_t length, 704 int32_t &resultCapacity) { 705 H *p; 706 if(needToRelease) { 707 p=ptr; 708 } else { 709 if(length<0) { 710 length=0; 711 } else if(length>capacity) { 712 length=capacity; 713 } 714 #if U_DEBUG && defined(UPRV_MALLOC_COUNT) 715 ::fprintf(::stderr,"MaybeStackHeaderAndArray (orphan) alloc %ul + %d * %lu\n", sizeof(H),length,sizeof(T)); 716 #endif 717 p=(H *)uprv_malloc(sizeof(H)+length*sizeof(T)); 718 if(p==nullptr) { 719 return nullptr; 720 } 721 uprv_memcpy(p, ptr, sizeof(H)+(size_t)length*sizeof(T)); 722 } 723 resultCapacity=length; 724 ptr=&stackHeader; 725 capacity=stackCapacity; 726 needToRelease=false; 727 return p; 728 } 729 730 /** 731 * A simple memory management class that creates new heap allocated objects (of 732 * any class that has a public constructor), keeps track of them and eventually 733 * deletes them all in its own destructor. 734 * 735 * A typical use-case would be code like this: 736 * 737 * MemoryPool<MyType> pool; 738 * 739 * MyType* o1 = pool.create(); 740 * if (o1 != nullptr) { 741 * foo(o1); 742 * } 743 * 744 * MyType* o2 = pool.create(1, 2, 3); 745 * if (o2 != nullptr) { 746 * bar(o2); 747 * } 748 * 749 * // MemoryPool will take care of deleting the MyType objects. 750 * 751 * It doesn't do anything more than that, and is intentionally kept minimalist. 752 */ 753 template<typename T, int32_t stackCapacity = 8> 754 class MemoryPool : public UMemory { 755 public: 756 MemoryPool() : fCount(0), fPool() {} 757 758 ~MemoryPool() { 759 for (int32_t i = 0; i < fCount; ++i) { 760 delete fPool[i]; 761 } 762 } 763 764 MemoryPool(const MemoryPool&) = delete; 765 MemoryPool& operator=(const MemoryPool&) = delete; 766 767 MemoryPool(MemoryPool&& other) noexcept : fCount(other.fCount), 768 fPool(std::move(other.fPool)) { 769 other.fCount = 0; 770 } 771 772 MemoryPool& operator=(MemoryPool&& other) noexcept { 773 // Since `this` may contain instances that need to be deleted, we can't 774 // just throw them away and replace them with `other`. The normal way of 775 // dealing with this in C++ is to swap `this` and `other`, rather than 776 // simply overwrite: the destruction of `other` can then take care of 777 // running MemoryPool::~MemoryPool() over the still-to-be-deallocated 778 // instances. 779 std::swap(fCount, other.fCount); 780 std::swap(fPool, other.fPool); 781 return *this; 782 } 783 784 /** 785 * Creates a new object of typename T, by forwarding any and all arguments 786 * to the typename T constructor. 787 * 788 * @param args Arguments to be forwarded to the typename T constructor. 789 * @return A pointer to the newly created object, or nullptr on error. 790 */ 791 template<typename... Args> 792 T* create(Args&&... args) { 793 int32_t capacity = fPool.getCapacity(); 794 if (fCount == capacity && 795 fPool.resize(capacity == stackCapacity ? 4 * capacity : 2 * capacity, 796 capacity) == nullptr) { 797 return nullptr; 798 } 799 return fPool[fCount++] = new T(std::forward<Args>(args)...); 800 } 801 802 template <typename... Args> 803 T* createAndCheckErrorCode(UErrorCode &status, Args &&... args) { 804 if (U_FAILURE(status)) { 805 return nullptr; 806 } 807 T *pointer = this->create(args...); 808 if (U_SUCCESS(status) && pointer == nullptr) { 809 status = U_MEMORY_ALLOCATION_ERROR; 810 } 811 return pointer; 812 } 813 814 /** 815 * @return Number of elements that have been allocated. 816 */ 817 int32_t count() const { 818 return fCount; 819 } 820 821 protected: 822 int32_t fCount; 823 MaybeStackArray<T*, stackCapacity> fPool; 824 }; 825 826 /** 827 * An internal Vector-like implementation based on MemoryPool. 828 * 829 * Heap-allocates each element and stores pointers. 830 * 831 * To append an item to the vector, use emplaceBack. 832 * 833 * MaybeStackVector<MyType> vector; 834 * MyType* element = vector.emplaceBack(); 835 * if (!element) { 836 * status = U_MEMORY_ALLOCATION_ERROR; 837 * } 838 * // do stuff with element 839 * 840 * To loop over the vector, use a for loop with indices: 841 * 842 * for (int32_t i = 0; i < vector.length(); i++) { 843 * MyType* element = vector[i]; 844 * } 845 */ 846 template<typename T, int32_t stackCapacity = 8> 847 class MaybeStackVector : protected MemoryPool<T, stackCapacity> { 848 public: 849 template<typename... Args> 850 T* emplaceBack(Args&&... args) { 851 return this->create(args...); 852 } 853 854 template <typename... Args> 855 T *emplaceBackAndCheckErrorCode(UErrorCode &status, Args &&... args) { 856 return this->createAndCheckErrorCode(status, args...); 857 } 858 859 int32_t length() const { 860 return this->fCount; 861 } 862 863 T** getAlias() { 864 return this->fPool.getAlias(); 865 } 866 867 const T *const *getAlias() const { 868 return this->fPool.getAlias(); 869 } 870 871 /** 872 * Array item access (read-only). 873 * No index bounds check. 874 * @param i array index 875 * @return reference to the array item 876 */ 877 const T* operator[](ptrdiff_t i) const { 878 return this->fPool[i]; 879 } 880 881 /** 882 * Array item access (writable). 883 * No index bounds check. 884 * @param i array index 885 * @return reference to the array item 886 */ 887 T* operator[](ptrdiff_t i) { 888 return this->fPool[i]; 889 } 890 }; 891 892 893 U_NAMESPACE_END 894 895 #endif /* __cplusplus */ 896 #endif /* CMEMORY_H */