1 /* melder_alloc.cpp
2  *
3  * Copyright (C) 1992-2007,2009,2011,2012,2014-2020 Paul Boersma
4  *
5  * This code is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or (at
8  * your option) any later version.
9  *
10  * This code is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13  * See the GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this work. If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #include "melder.h"
20 #include <wctype.h>
21 #include <assert.h>
22 
23 static int64 totalNumberOfAllocations = 0, totalNumberOfDeallocations = 0, totalAllocationSize = 0,
24 	totalNumberOfMovingReallocs = 0, totalNumberOfReallocsInSitu = 0;
25 
26 /*
27  * The rainy-day fund.
28  *
29  * Typically, memory allocation for data is entirely checked by using the normal versions of the allocation routines,
30  * which will call Melder_error if they are out of memory.
31  * When data allocation is indeed out of memory,
32  * the application will present an error message to the user saying that the data could not be created.
33  *
34  * By contrast, it is not practical to check the allocation of user interface elements,
35  * because the application cannot perform correctly if an interface element is missing or incorrect.
36  * For such situations, the application will typically use the _f versions of the allocation routines,
37  * which, if out of memory, will free a "rainy-day fund" and retry.
38  * In this way, the interface element can usually still be allocated;
39  * the application will present an error message telling the user to save her work and quit the application.
40  * If the user doesn't do that, the application will crash upon the next failing allocation of a _f routine.
41  */
42 
43 #define theRainyDayFund_SIZE  3'000'000
44 static char *theRainyDayFund = nullptr;
45 
Melder_alloc_init()46 void Melder_alloc_init () {
47 	theRainyDayFund = (char *) malloc (theRainyDayFund_SIZE);   // called at application initialization, so cannot fail
48 	assert (theRainyDayFund);
49 }
50 
51 /*
52 	The following functions take int64 arguments even on 32-bit machines.
53 	This is because it is easy for the user to request objects that do not fit in memory
54 	on 32-bit machines, in which case an appropriate error message is required.
55 */
56 
_Melder_malloc(int64 size)57 void * _Melder_malloc (int64 size) {
58 	if (size <= 0)
59 		Melder_throw (U"Can never allocate ", Melder_bigInteger (size), U" bytes.");
60 	if (sizeof (size_t) < 8 && size > SIZE_MAX)
61 		Melder_throw (U"Can never allocate ", Melder_bigInteger (size), U" bytes. Use a 64-bit edition of Praat instead?");
62 	void *result = malloc ((size_t) size);   // guarded cast
63 	if (! result)
64 		Melder_throw (U"Out of memory: there is not enough room for another ", Melder_bigInteger (size), U" bytes.");
65 	if (Melder_debug == 34)
66 		Melder_casual (U"Melder_malloc\t", Melder_pointer (result), U"\t", Melder_bigInteger (size), U"\t1");
67 	totalNumberOfAllocations += 1;
68 	totalAllocationSize += size;
69 	return result;
70 }
71 
_Melder_malloc_f(int64 size)72 void * _Melder_malloc_f (int64 size) {
73 	if (size <= 0)
74 		Melder_fatal (U"(Melder_malloc_f:) Can never allocate ", Melder_bigInteger (size), U" bytes.");
75 	if (sizeof (size_t) < 8 && size > SIZE_MAX)
76 		Melder_fatal (U"(Melder_malloc_f:) Can never allocate ", Melder_bigInteger (size), U" bytes.");
77 	void *result = malloc ((size_t) size);
78 	if (! result) {
79 		if (theRainyDayFund) {
80 			free (theRainyDayFund);
81 			theRainyDayFund = nullptr;
82 		}
83 		result = malloc ((size_t) size);
84 		if (result)
85 			Melder_flushError (U"Praat is very low on memory.\nSave your work and quit Praat.\nIf you don't do that, Praat may crash.");
86 		else
87 			Melder_fatal (U"Out of memory: there is not enough room for another ", Melder_bigInteger (size), U" bytes.");
88 	}
89 	totalNumberOfAllocations += 1;
90 	totalAllocationSize += size;
91 	return result;
92 }
93 
_Melder_free(void ** ptr)94 void _Melder_free (void **ptr) noexcept {
95 	if (! *ptr)
96 		return;
97 	if (Melder_debug == 34)
98 		Melder_casual (U"Melder_free\t", Melder_pointer (*ptr), U"\t?\t?");
99 	free (*ptr);
100 	*ptr = nullptr;
101 	totalNumberOfDeallocations += 1;
102 }
103 
Melder_realloc(void * ptr,int64 size)104 void * Melder_realloc (void *ptr, int64 size) {
105 	if (size <= 0)
106 		Melder_throw (U"Can never allocate ", Melder_bigInteger (size), U" bytes.");
107 	if (sizeof (size_t) < 8 && size > SIZE_MAX)
108 		Melder_throw (U"Can never allocate ", Melder_bigInteger (size), U" bytes. Use a 64-bit edition of Praat instead?");
109 	void *result = realloc (ptr, (size_t) size);   // will not show in the statistics...
110 	if (! result)
111 		Melder_throw (U"Out of memory. Could not extend room to ", Melder_bigInteger (size), U" bytes.");
112 	if (! ptr) {   // is it like malloc?
113 		if (Melder_debug == 34)
114 			Melder_casual (U"Melder_realloc\t", Melder_pointer (result), U"\t", Melder_bigInteger (size), U"\t1");
115 		totalNumberOfAllocations += 1;
116 		totalAllocationSize += size;
117 	} else if (result != ptr) {   // did realloc do a malloc-and-free?
118 		totalNumberOfAllocations += 1;
119 		totalAllocationSize += size;
120 		totalNumberOfDeallocations += 1;
121 		totalNumberOfMovingReallocs += 1;
122 	} else {
123 		totalNumberOfReallocsInSitu += 1;
124 	}
125 	return result;
126 }
127 
Melder_realloc_f(void * ptr,int64 size)128 void * Melder_realloc_f (void *ptr, int64 size) {
129 	if (size <= 0)
130 		Melder_fatal (U"(Melder_realloc_f:) Can never allocate ", Melder_bigInteger (size), U" bytes.");
131 	if (sizeof (size_t) < 8 && size > SIZE_MAX)
132 		Melder_fatal (U"(Melder_realloc_f:) Can never allocate ", Melder_bigInteger (size), U" bytes.");
133 	void *result = realloc (ptr, (size_t) size);   // will not show in the statistics...
134 	if (! result) {
135 		if (theRainyDayFund) {
136 			free (theRainyDayFund);
137 			theRainyDayFund = nullptr;
138 		}
139 		result = realloc (ptr, (size_t) size);
140 		if (result)
141 			Melder_flushError (U"Praat is very low on memory.\nSave your work and quit Praat.\nIf you don't do that, Praat may crash.");
142 		else
143 			Melder_fatal (U"Out of memory. Could not extend room to ", Melder_bigInteger (size), U" bytes.");
144 	}
145 	if (! ptr) {   // is it like malloc?
146 		totalNumberOfAllocations += 1;
147 		totalAllocationSize += size;
148 	} else if (result != ptr) {   // did realloc do a malloc-and-free?
149 		totalNumberOfAllocations += 1;
150 		totalAllocationSize += size;
151 		totalNumberOfDeallocations += 1;
152 		totalNumberOfMovingReallocs += 1;
153 	} else {
154 		totalNumberOfReallocsInSitu += 1;
155 	}
156 	return result;
157 }
158 
_Melder_calloc(int64 nelem,int64 elsize)159 void * _Melder_calloc (int64 nelem, int64 elsize) {
160 	if (nelem <= 0)
161 		Melder_throw (U"Can never allocate ", Melder_bigInteger (nelem), U" elements.");
162 	if (elsize <= 0)
163 		Melder_throw (U"Can never allocate elements whose size is ", Melder_bigInteger (elsize), U" bytes.");
164 	if ((uint64) nelem > SIZE_MAX / (uint64) elsize)   // guarded casts to unsigned
165 		Melder_throw (U"Can never allocate ", Melder_bigInteger (nelem), U" elements whose sizes are ", Melder_bigInteger (elsize), U" bytes each.",
166 			sizeof (size_t) < 8 ? U" Use a 64-bit edition of Praat instead?" : nullptr);
167 	void *result = calloc ((size_t) nelem, (size_t) elsize);
168 	if (! result)
169 		Melder_throw (U"Out of memory: there is not enough room for ", Melder_bigInteger (nelem), U" more elements whose sizes are ", elsize, U" bytes each.");
170 	if (Melder_debug == 34)
171 		Melder_casual (U"Melder_calloc\t", Melder_pointer (result), U"\t", Melder_bigInteger (nelem), U"\t", Melder_bigInteger (elsize));
172 	totalNumberOfAllocations += 1;
173 	totalAllocationSize += nelem * elsize;
174 	return result;
175 }
176 
_Melder_calloc_f(int64 nelem,int64 elsize)177 void * _Melder_calloc_f (int64 nelem, int64 elsize) {
178 	if (nelem <= 0)
179 		Melder_fatal (U"(Melder_calloc_f:) Can never allocate ", Melder_bigInteger (nelem), U" elements.");
180 	if (elsize <= 0)
181 		Melder_fatal (U"(Melder_calloc_f:) Can never allocate elements whose size is ", Melder_bigInteger (elsize), U" bytes.");
182 	if ((uint64) nelem > SIZE_MAX / (uint64) elsize)
183 		Melder_fatal (U"(Melder_calloc_f:) Can never allocate ", Melder_bigInteger (nelem), U" elements whose sizes are ", Melder_bigInteger (elsize), U" bytes each.");
184 	void *result = calloc ((size_t) nelem, (size_t) elsize);
185 	if (! result) {
186 		if (theRainyDayFund) {
187 			free (theRainyDayFund);
188 			theRainyDayFund = nullptr;
189 		}
190 		result = calloc ((size_t) nelem, (size_t) elsize);
191 		if (result)
192 			Melder_flushError (U"Praat is very low on memory.\nSave your work and quit Praat.\nIf you don't do that, Praat may crash.");
193 		else
194 			Melder_fatal (U"Out of memory: there is not enough room for ", Melder_bigInteger (nelem),
195 				U" more elements whose sizes are ", Melder_bigInteger (elsize), U" bytes each.");
196 	}
197 	totalNumberOfAllocations += 1;
198 	totalAllocationSize += nelem * elsize;
199 	return result;
200 }
201 
Melder_dup(conststring32 string)202 autostring32 Melder_dup (conststring32 string /* cattable */) {
203 	if (! string)
204 		return autostring32();
205 	int64 size = (int64) str32len (string) + 1;   // guaranteed to be positive
206 	if (sizeof (size_t) < 8 && size > SIZE_MAX / sizeof (char32))
207 		Melder_throw (U"Can never allocate ", Melder_bigInteger (size), U" characters. Use a 64-bit edition of Praat instead?");
208 	autostring32 result (size, false);   // guarded conversion
209 	str32cpy (result.get(), string);
210 	if (Melder_debug == 34)
211 		Melder_casual (U"Melder_dup\t", Melder_pointer (result.get()), U"\t", Melder_bigInteger (size), U"\t", sizeof (char32));
212 	return result;
213 }
214 
Melder_dup_f(conststring32 string)215 autostring32 Melder_dup_f (conststring32 string /* cattable */) {
216 	if (! string)
217 		return autostring32();
218 	int64 size = (int64) str32len (string) + 1;
219 	if (sizeof (size_t) < 8 && size > SIZE_MAX / sizeof (char32))
220 		Melder_fatal (U"(Melder_dup_f:) Can never allocate ", Melder_bigInteger (size), U" characters.");
221 	autostring32 result (size, true);
222 	str32cpy (result.get(), string);
223 	return result;
224 }
225 
Melder_allocationCount()226 int64 Melder_allocationCount () {
227 	return totalNumberOfAllocations;
228 }
229 
Melder_deallocationCount()230 int64 Melder_deallocationCount () {
231 	return totalNumberOfDeallocations;
232 }
233 
Melder_allocationSize()234 int64 Melder_allocationSize () {
235 	return totalAllocationSize;
236 }
237 
Melder_reallocationsInSituCount()238 int64 Melder_reallocationsInSituCount () {
239 	return totalNumberOfReallocsInSitu;
240 }
241 
Melder_movingReallocationsCount()242 int64 Melder_movingReallocationsCount () {
243 	return totalNumberOfMovingReallocs;
244 }
245 
Melder_cmp(conststring32 string1,conststring32 string2)246 int Melder_cmp (conststring32 string1, conststring32 string2) {
247 	if (! string1) string1 = U"";
248 	if (! string2) string2 = U"";
249 	return str32cmp (string1, string2);
250 }
251 
Melder_cmp_caseInsensitive(conststring32 string1,conststring32 string2)252 int Melder_cmp_caseInsensitive (conststring32 string1, conststring32 string2) {
253 	if (! string1) string1 = U"";
254 	if (! string2) string2 = U"";
255 	return str32cmp_caseInsensitive (string1, string2);
256 }
257 
Melder_ncmp(conststring32 string1,conststring32 string2,integer n)258 int Melder_ncmp (conststring32 string1, conststring32 string2, integer n) {
259 	if (! string1) string1 = U"";
260 	if (! string2) string2 = U"";
261 	return str32ncmp (string1, string2, n);
262 }
263 
Melder_ncmp_caseInsensitive(conststring32 string1,conststring32 string2,integer n)264 int Melder_ncmp_caseInsensitive (conststring32 string1, conststring32 string2, integer n) {
265 	if (! string1) string1 = U"";
266 	if (! string2) string2 = U"";
267 	return str32ncmp_caseInsensitive (string1, string2, n);
268 }
269 
Melder_equ_firstCharacterCaseInsensitive(conststring32 string1,conststring32 string2)270 bool Melder_equ_firstCharacterCaseInsensitive (conststring32 string1, conststring32 string2) {
271 	if (! string1) string1 = U"";
272 	if (! string2) string2 = U"";
273 	if (string1 [0] == U'\0')
274 		return string2 [0] == U'\0';
275 	if (Melder_toLowerCase (string1 [0]) != Melder_toLowerCase (string2 [0]))
276 		return false;
277 	return str32equ (string1 + 1, string2 + 1);
278 }
279 
280 #pragma mark - Generic memory functions for vectors and matrices
281 
282 namespace MelderArray { // reopen
283 	int64 allocationCount = 0, deallocationCount = 0;
284 	int64 cellAllocationCount = 0, cellDeallocationCount = 0;
285 }
286 
MelderArray_allocationCount()287 int64 MelderArray_allocationCount () { return MelderArray :: allocationCount; }
MelderArray_deallocationCount()288 int64 MelderArray_deallocationCount () { return MelderArray :: deallocationCount; }
MelderArray_cellAllocationCount()289 int64 MelderArray_cellAllocationCount () { return MelderArray :: cellAllocationCount; }
MelderArray_cellDeallocationCount()290 int64 MelderArray_cellDeallocationCount () { return MelderArray :: cellDeallocationCount; }
291 
_alloc_generic(integer cellSize,integer numberOfCells,kInitializationType initializationType)292 byte * MelderArray:: _alloc_generic (integer cellSize, integer numberOfCells, kInitializationType initializationType) {
293 	try {
294 		if (numberOfCells <= 0)
295 			return nullptr;   // not an error
296 		byte *result = ( initializationType == kInitializationType :: ZERO ?
297 				reinterpret_cast <byte *> (_Melder_calloc (numberOfCells, cellSize)) :
298 				reinterpret_cast <byte *> (_Melder_malloc (numberOfCells * cellSize)) );
299 		MelderArray::allocationCount += 1;
300 		MelderArray::cellAllocationCount += numberOfCells;
301 		return result;
302 	} catch (MelderError) {
303 		Melder_throw (U"Tensor of ", numberOfCells, U" cells not created.");
304 	}
305 }
306 
_free_generic(byte * cells,integer numberOfCells)307 void MelderArray:: _free_generic (byte *cells, integer numberOfCells) noexcept {
308 	if (! cells)
309 		return;   // not an error
310 	Melder_free (cells);
311 	MelderArray::deallocationCount += 1;
312 	MelderArray::cellDeallocationCount += numberOfCells;
313 }
314 
315 /* End of file melder_alloc.cpp */
316