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
104U_CAPI void * U_EXPORT2
105uprv_malloc(size_t s) U_MALLOC_ATTR U_ALLOC_SIZE_ATTR(1);
106
107U_CAPI void * U_EXPORT2
108uprv_realloc(void *mem, size_t size) U_ALLOC_SIZE_ATTR(2);
109
110U_CAPI void U_EXPORT2
111uprv_free(void *mem);
112
113U_CAPI void * U_EXPORT2
114uprv_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 */
153U_CFUNC UBool
154cmemory_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 */
163typedef 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 */
169U_CAPI void U_EXPORT2
170uprv_deleteUObject(void *obj);
171
172#ifdef __cplusplus
173
174#include <utility>
175#include "unicode/uobject.h"
176
177U_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 */
186template<typename T>
187class LocalMemory : public LocalPointerBase<T> {
188public:
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
277template<typename T>
278inline 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
293template<typename T>
294inline 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 */
331template<typename T, int32_t stackCapacity>
332class MaybeStackArray {
333public:
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#if U_HAVE_PLACEMENT_NEW
338 static void* U_EXPORT2 operator new(size_t, void*) noexcept = delete;
339#endif
340
341 /**
342 * Default constructor initializes with internal T[stackCapacity] buffer.
343 */
344 MaybeStackArray() : ptr(stackArray), capacity(stackCapacity), needToRelease(false) {}
345 /**
346 * Automatically allocates the heap array if the argument is larger than the stack capacity.
347 * Intended for use when an approximate capacity is known at compile time but the true
348 * capacity is not known until runtime.
349 */
350 MaybeStackArray(int32_t newCapacity, UErrorCode status) : MaybeStackArray() {
351 if (U_FAILURE(status)) {
352 return;
353 }
354 if (capacity < newCapacity) {
355 if (resize(newCapacity) == nullptr) {
356 status = U_MEMORY_ALLOCATION_ERROR;
357 }
358 }
359 }
360 /**
361 * Destructor deletes the array (if owned).
362 */
363 ~MaybeStackArray() { releaseArray(); }
364 /**
365 * Move constructor: transfers ownership or copies the stack array.
366 */
367 MaybeStackArray(MaybeStackArray<T, stackCapacity> &&src) noexcept;
368 /**
369 * Move assignment: transfers ownership or copies the stack array.
370 */
371 MaybeStackArray<T, stackCapacity> &operator=(MaybeStackArray<T, stackCapacity> &&src) noexcept;
372 /**
373 * Returns the array capacity (number of T items).
374 * @return array capacity
375 */
376 int32_t getCapacity() const { return capacity; }
377 /**
378 * Access without ownership change.
379 * @return the array pointer
380 */
381 T *getAlias() const { return ptr; }
382 /**
383 * Returns the array limit. Simple convenience method.
384 * @return getAlias()+getCapacity()
385 */
386 T *getArrayLimit() const { return getAlias()+capacity; }
387 // No "operator T *() const" because that can make
388 // expressions like mbs[index] ambiguous for some compilers.
389 /**
390 * Array item access (const).
391 * No index bounds check.
392 * @param i array index
393 * @return reference to the array item
394 */
395 const T &operator[](ptrdiff_t i) const { return ptr[i]; }
396 /**
397 * Array item access (writable).
398 * No index bounds check.
399 * @param i array index
400 * @return reference to the array item
401 */
402 T &operator[](ptrdiff_t i) { return ptr[i]; }
403 /**
404 * Deletes the array (if owned) and aliases another one, no transfer of ownership.
405 * If the arguments are illegal, then the current array is unchanged.
406 * @param otherArray must not be nullptr
407 * @param otherCapacity must be >0
408 */
409 void aliasInstead(T *otherArray, int32_t otherCapacity) {
410 if(otherArray!=nullptr && otherCapacity>0) {
411 releaseArray();
412 ptr=otherArray;
413 capacity=otherCapacity;
414 needToRelease=false;
415 }
416 }
417 /**
418 * Deletes the array (if owned) and allocates a new one, copying length T items.
419 * Returns the new array pointer.
420 * If the allocation fails, then the current array is unchanged and
421 * this method returns nullptr.
422 * @param newCapacity can be less than or greater than the current capacity;
423 * must be >0
424 * @param length number of T items to be copied from the old array to the new one
425 * @return the allocated array pointer, or nullptr if the allocation failed
426 */
427 inline T *resize(int32_t newCapacity, int32_t length=0);
428 /**
429 * Gives up ownership of the array if owned, or else clones it,
430 * copying length T items; resets itself to the internal stack array.
431 * Returns nullptr if the allocation failed.
432 * @param length number of T items to copy when cloning,
433 * and capacity of the clone when cloning
434 * @param resultCapacity will be set to the returned array's capacity (output-only)
435 * @return the array pointer;
436 * caller becomes responsible for deleting the array
437 */
438 inline T *orphanOrClone(int32_t length, int32_t &resultCapacity);
439
440protected:
441 // Resizes the array to the size of src, then copies the contents of src.
442 void copyFrom(const MaybeStackArray &src, UErrorCode &status) {
443 if (U_FAILURE(status)) {
444 return;
445 }
446 if (this->resize(src.capacity, 0) == nullptr) {
447 status = U_MEMORY_ALLOCATION_ERROR;
448 return;
449 }
450 uprv_memcpy(this->ptr, src.ptr, (size_t)capacity * sizeof(T));
451 }
452
453private:
454 T *ptr;
455 int32_t capacity;
456 UBool needToRelease;
457 T stackArray[stackCapacity];
458 void releaseArray() {
459 if(needToRelease) {
460 uprv_free(ptr);
461 }
462 }
463 void resetToStackArray() {
464 ptr=stackArray;
465 capacity=stackCapacity;
466 needToRelease=false;
467 }
468 /* No comparison operators with other MaybeStackArray's. */
469 bool operator==(const MaybeStackArray & /*other*/) = delete;
470 bool operator!=(const MaybeStackArray & /*other*/) = delete;
471 /* No ownership transfer: No copy constructor, no assignment operator. */
472 MaybeStackArray(const MaybeStackArray & /*other*/) = delete;
473 void operator=(const MaybeStackArray & /*other*/) = delete;
474};
475
476template<typename T, int32_t stackCapacity>
477icu::MaybeStackArray<T, stackCapacity>::MaybeStackArray(
478 MaybeStackArray <T, stackCapacity>&& src) noexcept
479 : ptr(src.ptr), capacity(src.capacity), needToRelease(src.needToRelease) {
480 if (src.ptr == src.stackArray) {
481 ptr = stackArray;
482 uprv_memcpy(stackArray, src.stackArray, sizeof(T) * src.capacity);
483 } else {
484 src.resetToStackArray(); // take ownership away from src
485 }
486}
487
488template<typename T, int32_t stackCapacity>
489inline MaybeStackArray <T, stackCapacity>&
490MaybeStackArray<T, stackCapacity>::operator=(MaybeStackArray <T, stackCapacity>&& src) noexcept {
491 releaseArray(); // in case this instance had its own memory allocated
492 capacity = src.capacity;
493 needToRelease = src.needToRelease;
494 if (src.ptr == src.stackArray) {
495 ptr = stackArray;
496 uprv_memcpy(stackArray, src.stackArray, sizeof(T) * src.capacity);
497 } else {
498 ptr = src.ptr;
499 src.resetToStackArray(); // take ownership away from src
500 }
501 return *this;
502}
503
504template<typename T, int32_t stackCapacity>
505inline T *MaybeStackArray<T, stackCapacity>::resize(int32_t newCapacity, int32_t length) {
506 if(newCapacity>0) {
507#if U_DEBUG && defined(UPRV_MALLOC_COUNT)
508 ::fprintf(::stderr, "MaybeStackArray (resize) alloc %d * %lu\n", newCapacity, sizeof(T));
509#endif
510 T *p=(T *)uprv_malloc(newCapacity*sizeof(T));
511 if(p!=nullptr) {
512 if(length>0) {
513 if(length>capacity) {
514 length=capacity;
515 }
516 if(length>newCapacity) {
517 length=newCapacity;
518 }
519 uprv_memcpy(p, ptr, (size_t)length*sizeof(T));
520 }
521 releaseArray();
522 ptr=p;
523 capacity=newCapacity;
524 needToRelease=true;
525 }
526 return p;
527 } else {
528 return nullptr;
529 }
530}
531
532template<typename T, int32_t stackCapacity>
533inline T *MaybeStackArray<T, stackCapacity>::orphanOrClone(int32_t length, int32_t &resultCapacity) {
534 T *p;
535 if(needToRelease) {
536 p=ptr;
537 } else if(length<=0) {
538 return nullptr;
539 } else {
540 if(length>capacity) {
541 length=capacity;
542 }
543 p=(T *)uprv_malloc(length*sizeof(T));
544#if U_DEBUG && defined(UPRV_MALLOC_COUNT)
545 ::fprintf(::stderr,"MaybeStacArray (orphan) alloc %d * %lu\n", length,sizeof(T));
546#endif
547 if(p==nullptr) {
548 return nullptr;
549 }
550 uprv_memcpy(p, ptr, (size_t)length*sizeof(T));
551 }
552 resultCapacity=length;
553 resetToStackArray();
554 return p;
555}
556
557/**
558 * Variant of MaybeStackArray that allocates a header struct and an array
559 * in one contiguous memory block, using uprv_malloc() and uprv_free().
560 * Provides internal memory with fixed array capacity. Can alias another memory
561 * block or allocate one.
562 * The stackCapacity is the number of T items in the internal memory,
563 * not counting the H header.
564 * Unlike LocalMemory and LocalArray, this class never adopts
565 * (takes ownership of) another memory block.
566 */
567template<typename H, typename T, int32_t stackCapacity>
568class MaybeStackHeaderAndArray {
569public:
570 // No heap allocation. Use only on the stack.
571 static void* U_EXPORT2 operator new(size_t) noexcept = delete;
572 static void* U_EXPORT2 operator new[](size_t) noexcept = delete;
573#if U_HAVE_PLACEMENT_NEW
574 static void* U_EXPORT2 operator new(size_t, void*) noexcept = delete;
575#endif
576
577 /**
578 * Default constructor initializes with internal H+T[stackCapacity] buffer.
579 */
580 MaybeStackHeaderAndArray() : ptr(&stackHeader), capacity(stackCapacity), needToRelease(false) {}
581 /**
582 * Destructor deletes the memory (if owned).
583 */
584 ~MaybeStackHeaderAndArray() { releaseMemory(); }
585 /**
586 * Returns the array capacity (number of T items).
587 * @return array capacity
588 */
589 int32_t getCapacity() const { return capacity; }
590 /**
591 * Access without ownership change.
592 * @return the header pointer
593 */
594 H *getAlias() const { return ptr; }
595 /**
596 * Returns the array start.
597 * @return array start, same address as getAlias()+1
598 */
599 T *getArrayStart() const { return reinterpret_cast<T *>(getAlias()+1); }
600 /**
601 * Returns the array limit.
602 * @return array limit
603 */
604 T *getArrayLimit() const { return getArrayStart()+capacity; }
605 /**
606 * Access without ownership change. Same as getAlias().
607 * A class instance can be used directly in expressions that take a T *.
608 * @return the header pointer
609 */
610 operator H *() const { return ptr; }
611 /**
612 * Array item access (writable).
613 * No index bounds check.
614 * @param i array index
615 * @return reference to the array item
616 */
617 T &operator[](ptrdiff_t i) { return getArrayStart()[i]; }
618 /**
619 * Deletes the memory block (if owned) and aliases another one, no transfer of ownership.
620 * If the arguments are illegal, then the current memory is unchanged.
621 * @param otherArray must not be nullptr
622 * @param otherCapacity must be >0
623 */
624 void aliasInstead(H *otherMemory, int32_t otherCapacity) {
625 if(otherMemory!=nullptr && otherCapacity>0) {
626 releaseMemory();
627 ptr=otherMemory;
628 capacity=otherCapacity;
629 needToRelease=false;
630 }
631 }
632 /**
633 * Deletes the memory block (if owned) and allocates a new one,
634 * copying the header and length T array items.
635 * Returns the new header pointer.
636 * If the allocation fails, then the current memory is unchanged and
637 * this method returns nullptr.
638 * @param newCapacity can be less than or greater than the current capacity;
639 * must be >0
640 * @param length number of T items to be copied from the old array to the new one
641 * @return the allocated pointer, or nullptr if the allocation failed
642 */
643 inline H *resize(int32_t newCapacity, int32_t length=0);
644 /**
645 * Gives up ownership of the memory if owned, or else clones it,
646 * copying the header and length T array items; resets itself to the internal memory.
647 * Returns nullptr if the allocation failed.
648 * @param length number of T items to copy when cloning,
649 * and array capacity of the clone when cloning
650 * @param resultCapacity will be set to the returned array's capacity (output-only)
651 * @return the header pointer;
652 * caller becomes responsible for deleting the array
653 */
654 inline H *orphanOrClone(int32_t length, int32_t &resultCapacity);
655private:
656 H *ptr;
657 int32_t capacity;
658 UBool needToRelease;
659 // stackHeader must precede stackArray immediately.
660 H stackHeader;
661 T stackArray[stackCapacity];
662 void releaseMemory() {
663 if(needToRelease) {
664 uprv_free(ptr);
665 }
666 }
667 /* No comparison operators with other MaybeStackHeaderAndArray's. */
668 bool operator==(const MaybeStackHeaderAndArray & /*other*/) {return false;}
669 bool operator!=(const MaybeStackHeaderAndArray & /*other*/) {return true;}
670 /* No ownership transfer: No copy constructor, no assignment operator. */
671 MaybeStackHeaderAndArray(const MaybeStackHeaderAndArray & /*other*/) {}
672 void operator=(const MaybeStackHeaderAndArray & /*other*/) {}
673};
674
675template<typename H, typename T, int32_t stackCapacity>
676inline H *MaybeStackHeaderAndArray<H, T, stackCapacity>::resize(int32_t newCapacity,
677 int32_t length) {
678 if(newCapacity>=0) {
679#if U_DEBUG && defined(UPRV_MALLOC_COUNT)
680 ::fprintf(::stderr,"MaybeStackHeaderAndArray alloc %d + %d * %ul\n", sizeof(H),newCapacity,sizeof(T));
681#endif
682 H *p=(H *)uprv_malloc(sizeof(H)+newCapacity*sizeof(T));
683 if(p!=nullptr) {
684 if(length<0) {
685 length=0;
686 } else if(length>0) {
687 if(length>capacity) {
688 length=capacity;
689 }
690 if(length>newCapacity) {
691 length=newCapacity;
692 }
693 }
694 uprv_memcpy(p, ptr, sizeof(H)+(size_t)length*sizeof(T));
695 releaseMemory();
696 ptr=p;
697 capacity=newCapacity;
698 needToRelease=true;
699 }
700 return p;
701 } else {
702 return nullptr;
703 }
704}
705
706template<typename H, typename T, int32_t stackCapacity>
707inline H *MaybeStackHeaderAndArray<H, T, stackCapacity>::orphanOrClone(int32_t length,
708 int32_t &resultCapacity) {
709 H *p;
710 if(needToRelease) {
711 p=ptr;
712 } else {
713 if(length<0) {
714 length=0;
715 } else if(length>capacity) {
716 length=capacity;
717 }
718#if U_DEBUG && defined(UPRV_MALLOC_COUNT)
719 ::fprintf(::stderr,"MaybeStackHeaderAndArray (orphan) alloc %ul + %d * %lu\n", sizeof(H),length,sizeof(T));
720#endif
721 p=(H *)uprv_malloc(sizeof(H)+length*sizeof(T));
722 if(p==nullptr) {
723 return nullptr;
724 }
725 uprv_memcpy(p, ptr, sizeof(H)+(size_t)length*sizeof(T));
726 }
727 resultCapacity=length;
728 ptr=&stackHeader;
729 capacity=stackCapacity;
730 needToRelease=false;
731 return p;
732}
733
734/**
735 * A simple memory management class that creates new heap allocated objects (of
736 * any class that has a public constructor), keeps track of them and eventually
737 * deletes them all in its own destructor.
738 *
739 * A typical use-case would be code like this:
740 *
741 * MemoryPool<MyType> pool;
742 *
743 * MyType* o1 = pool.create();
744 * if (o1 != nullptr) {
745 * foo(o1);
746 * }
747 *
748 * MyType* o2 = pool.create(1, 2, 3);
749 * if (o2 != nullptr) {
750 * bar(o2);
751 * }
752 *
753 * // MemoryPool will take care of deleting the MyType objects.
754 *
755 * It doesn't do anything more than that, and is intentionally kept minimalist.
756 */
757template<typename T, int32_t stackCapacity = 8>
758class MemoryPool : public UMemory {
759public:
760 MemoryPool() : fCount(0), fPool() {}
761
762 ~MemoryPool() {
763 for (int32_t i = 0; i < fCount; ++i) {
764 delete fPool[i];
765 }
766 }
767
768 MemoryPool(const MemoryPool&) = delete;
769 MemoryPool& operator=(const MemoryPool&) = delete;
770
771 MemoryPool(MemoryPool&& other) noexcept : fCount(other.fCount),
772 fPool(std::move(other.fPool)) {
773 other.fCount = 0;
774 }
775
776 MemoryPool& operator=(MemoryPool&& other) noexcept {
777 // Since `this` may contain instances that need to be deleted, we can't
778 // just throw them away and replace them with `other`. The normal way of
779 // dealing with this in C++ is to swap `this` and `other`, rather than
780 // simply overwrite: the destruction of `other` can then take care of
781 // running MemoryPool::~MemoryPool() over the still-to-be-deallocated
782 // instances.
783 std::swap(fCount, other.fCount);
784 std::swap(fPool, other.fPool);
785 return *this;
786 }
787
788 /**
789 * Creates a new object of typename T, by forwarding any and all arguments
790 * to the typename T constructor.
791 *
792 * @param args Arguments to be forwarded to the typename T constructor.
793 * @return A pointer to the newly created object, or nullptr on error.
794 */
795 template<typename... Args>
796 T* create(Args&&... args) {
797 int32_t capacity = fPool.getCapacity();
798 if (fCount == capacity &&
799 fPool.resize(capacity == stackCapacity ? 4 * capacity : 2 * capacity,
800 capacity) == nullptr) {
801 return nullptr;
802 }
803 return fPool[fCount++] = new T(std::forward<Args>(args)...);
804 }
805
806 template <typename... Args>
807 T* createAndCheckErrorCode(UErrorCode &status, Args &&... args) {
808 if (U_FAILURE(status)) {
809 return nullptr;
810 }
811 T *pointer = this->create(args...);
812 if (U_SUCCESS(status) && pointer == nullptr) {
813 status = U_MEMORY_ALLOCATION_ERROR;
814 }
815 return pointer;
816 }
817
818 /**
819 * @return Number of elements that have been allocated.
820 */
821 int32_t count() const {
822 return fCount;
823 }
824
825protected:
826 int32_t fCount;
827 MaybeStackArray<T*, stackCapacity> fPool;
828};
829
830/**
831 * An internal Vector-like implementation based on MemoryPool.
832 *
833 * Heap-allocates each element and stores pointers.
834 *
835 * To append an item to the vector, use emplaceBack.
836 *
837 * MaybeStackVector<MyType> vector;
838 * MyType* element = vector.emplaceBack();
839 * if (!element) {
840 * status = U_MEMORY_ALLOCATION_ERROR;
841 * }
842 * // do stuff with element
843 *
844 * To loop over the vector, use a for loop with indices:
845 *
846 * for (int32_t i = 0; i < vector.length(); i++) {
847 * MyType* element = vector[i];
848 * }
849 */
850template<typename T, int32_t stackCapacity = 8>
851class MaybeStackVector : protected MemoryPool<T, stackCapacity> {
852public:
853 template<typename... Args>
854 T* emplaceBack(Args&&... args) {
855 return this->create(args...);
856 }
857
858 template <typename... Args>
859 T *emplaceBackAndCheckErrorCode(UErrorCode &status, Args &&... args) {
860 return this->createAndCheckErrorCode(status, args...);
861 }
862
863 int32_t length() const {
864 return this->fCount;
865 }
866
867 T** getAlias() {
868 return this->fPool.getAlias();
869 }
870
871 const T *const *getAlias() const {
872 return this->fPool.getAlias();
873 }
874
875 /**
876 * Array item access (read-only).
877 * No index bounds check.
878 * @param i array index
879 * @return reference to the array item
880 */
881 const T* operator[](ptrdiff_t i) const {
882 return this->fPool[i];
883 }
884
885 /**
886 * Array item access (writable).
887 * No index bounds check.
888 * @param i array index
889 * @return reference to the array item
890 */
891 T* operator[](ptrdiff_t i) {
892 return this->fPool[i];
893 }
894};
895
896
897U_NAMESPACE_END
898
899#endif /* __cplusplus */
900#endif /* CMEMORY_H */
901