Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/icu4c/common/cmemory.h
21917 views
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 */
897
898