Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/icu4c/common/cmemory.h
9903 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
#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
440
protected:
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
453
private:
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
476
template<typename T, int32_t stackCapacity>
477
icu::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
488
template<typename T, int32_t stackCapacity>
489
inline MaybeStackArray <T, stackCapacity>&
490
MaybeStackArray<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
504
template<typename T, int32_t stackCapacity>
505
inline 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
532
template<typename T, int32_t stackCapacity>
533
inline 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
*/
567
template<typename H, typename T, int32_t stackCapacity>
568
class MaybeStackHeaderAndArray {
569
public:
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);
655
private:
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
675
template<typename H, typename T, int32_t stackCapacity>
676
inline 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
706
template<typename H, typename T, int32_t stackCapacity>
707
inline 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
*/
757
template<typename T, int32_t stackCapacity = 8>
758
class MemoryPool : public UMemory {
759
public:
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
825
protected:
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
*/
850
template<typename T, int32_t stackCapacity = 8>
851
class MaybeStackVector : protected MemoryPool<T, stackCapacity> {
852
public:
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
897
U_NAMESPACE_END
898
899
#endif /* __cplusplus */
900
#endif /* CMEMORY_H */
901
902