Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
4358 Serge 1
/*
2
 * Copyright © 2010 Intel Corporation
3
 *
4
 * Permission is hereby granted, free of charge, to any person obtaining a
5
 * copy of this software and associated documentation files (the "Software"),
6
 * to deal in the Software without restriction, including without limitation
7
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8
 * and/or sell copies of the Software, and to permit persons to whom the
9
 * Software is furnished to do so, subject to the following conditions:
10
 *
11
 * The above copyright notice and this permission notice (including the next
12
 * paragraph) shall be included in all copies or substantial portions of the
13
 * Software.
14
 *
15
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21
 * DEALINGS IN THE SOFTWARE.
22
 */
23
 
24
#include 
25
#include 
26
#include 
27
#include 
28
#include 
29
#include 
30
 
31
/* Android defines SIZE_MAX in limits.h, instead of the standard stdint.h */
32
#ifdef ANDROID
33
#include 
34
#endif
35
 
36
/* Some versions of MinGW are missing _vscprintf's declaration, although they
37
 * still provide the symbol in the import library. */
38
#ifdef __MINGW32__
39
_CRTIMP int _vscprintf(const char *format, va_list argptr);
40
#endif
41
 
42
#include "ralloc.h"
43
 
44
#ifndef va_copy
45
#ifdef __va_copy
46
#define va_copy(dest, src) __va_copy((dest), (src))
47
#else
48
#define va_copy(dest, src) (dest) = (src)
49
#endif
50
#endif
51
 
52
#define CANARY 0x5A1106
53
 
54
struct ralloc_header
55
{
56
   /* A canary value used to determine whether a pointer is ralloc'd. */
57
   unsigned canary;
58
 
59
   struct ralloc_header *parent;
60
 
61
   /* The first child (head of a linked list) */
62
   struct ralloc_header *child;
63
 
64
   /* Linked list of siblings */
65
   struct ralloc_header *prev;
66
   struct ralloc_header *next;
67
 
68
   void (*destructor)(void *);
69
};
70
 
71
typedef struct ralloc_header ralloc_header;
72
 
73
static void unlink_block(ralloc_header *info);
74
static void unsafe_free(ralloc_header *info);
75
 
76
static ralloc_header *
77
get_header(const void *ptr)
78
{
79
   ralloc_header *info = (ralloc_header *) (((char *) ptr) -
80
					    sizeof(ralloc_header));
81
   assert(info->canary == CANARY);
82
   return info;
83
}
84
 
85
#define PTR_FROM_HEADER(info) (((char *) info) + sizeof(ralloc_header))
86
 
87
static void
88
add_child(ralloc_header *parent, ralloc_header *info)
89
{
90
   if (parent != NULL) {
91
      info->parent = parent;
92
      info->next = parent->child;
93
      parent->child = info;
94
 
95
      if (info->next != NULL)
96
	 info->next->prev = info;
97
   }
98
}
99
 
100
void *
101
ralloc_context(const void *ctx)
102
{
103
   return ralloc_size(ctx, 0);
104
}
105
 
106
void *
107
ralloc_size(const void *ctx, size_t size)
108
{
109
   void *block = calloc(1, size + sizeof(ralloc_header));
110
   ralloc_header *info;
111
   ralloc_header *parent;
112
 
113
   if (unlikely(block == NULL))
114
      return NULL;
115
   info = (ralloc_header *) block;
116
   parent = ctx != NULL ? get_header(ctx) : NULL;
117
 
118
   add_child(parent, info);
119
 
120
   info->canary = CANARY;
121
 
122
   return PTR_FROM_HEADER(info);
123
}
124
 
125
void *
126
rzalloc_size(const void *ctx, size_t size)
127
{
128
   void *ptr = ralloc_size(ctx, size);
129
   if (likely(ptr != NULL))
130
      memset(ptr, 0, size);
131
   return ptr;
132
}
133
 
134
/* helper function - assumes ptr != NULL */
135
static void *
136
resize(void *ptr, size_t size)
137
{
138
   ralloc_header *child, *old, *info;
139
 
140
   old = get_header(ptr);
141
   info = realloc(old, size + sizeof(ralloc_header));
142
 
143
   if (info == NULL)
144
      return NULL;
145
 
146
   /* Update parent and sibling's links to the reallocated node. */
147
   if (info != old && info->parent != NULL) {
148
      if (info->parent->child == old)
149
	 info->parent->child = info;
150
 
151
      if (info->prev != NULL)
152
	 info->prev->next = info;
153
 
154
      if (info->next != NULL)
155
	 info->next->prev = info;
156
   }
157
 
158
   /* Update child->parent links for all children */
159
   for (child = info->child; child != NULL; child = child->next)
160
      child->parent = info;
161
 
162
   return PTR_FROM_HEADER(info);
163
}
164
 
165
void *
166
reralloc_size(const void *ctx, void *ptr, size_t size)
167
{
168
   if (unlikely(ptr == NULL))
169
      return ralloc_size(ctx, size);
170
 
171
   assert(ralloc_parent(ptr) == ctx);
172
   return resize(ptr, size);
173
}
174
 
175
void *
176
ralloc_array_size(const void *ctx, size_t size, unsigned count)
177
{
178
   if (count > SIZE_MAX/size)
179
      return NULL;
180
 
181
   return ralloc_size(ctx, size * count);
182
}
183
 
184
void *
185
rzalloc_array_size(const void *ctx, size_t size, unsigned count)
186
{
187
   if (count > SIZE_MAX/size)
188
      return NULL;
189
 
190
   return rzalloc_size(ctx, size * count);
191
}
192
 
193
void *
194
reralloc_array_size(const void *ctx, void *ptr, size_t size, unsigned count)
195
{
196
   if (count > SIZE_MAX/size)
197
      return NULL;
198
 
199
   return reralloc_size(ctx, ptr, size * count);
200
}
201
 
202
void
203
ralloc_free(void *ptr)
204
{
205
   ralloc_header *info;
206
 
207
   if (ptr == NULL)
208
      return;
209
 
210
   info = get_header(ptr);
211
   unlink_block(info);
212
   unsafe_free(info);
213
}
214
 
215
static void
216
unlink_block(ralloc_header *info)
217
{
218
   /* Unlink from parent & siblings */
219
   if (info->parent != NULL) {
220
      if (info->parent->child == info)
221
	 info->parent->child = info->next;
222
 
223
      if (info->prev != NULL)
224
	 info->prev->next = info->next;
225
 
226
      if (info->next != NULL)
227
	 info->next->prev = info->prev;
228
   }
229
   info->parent = NULL;
230
   info->prev = NULL;
231
   info->next = NULL;
232
}
233
 
234
static void
235
unsafe_free(ralloc_header *info)
236
{
237
   /* Recursively free any children...don't waste time unlinking them. */
238
   ralloc_header *temp;
239
   while (info->child != NULL) {
240
      temp = info->child;
241
      info->child = temp->next;
242
      unsafe_free(temp);
243
   }
244
 
245
   /* Free the block itself.  Call the destructor first, if any. */
246
   if (info->destructor != NULL)
247
      info->destructor(PTR_FROM_HEADER(info));
248
 
249
   free(info);
250
}
251
 
252
void
253
ralloc_steal(const void *new_ctx, void *ptr)
254
{
255
   ralloc_header *info, *parent;
256
 
257
   if (unlikely(ptr == NULL))
258
      return;
259
 
260
   info = get_header(ptr);
261
   parent = get_header(new_ctx);
262
 
263
   unlink_block(info);
264
 
265
   add_child(parent, info);
266
}
267
 
268
void *
269
ralloc_parent(const void *ptr)
270
{
271
   ralloc_header *info;
272
 
273
   if (unlikely(ptr == NULL))
274
      return NULL;
275
 
276
   info = get_header(ptr);
277
   return info->parent ? PTR_FROM_HEADER(info->parent) : NULL;
278
}
279
 
280
static void *autofree_context = NULL;
281
 
282
static void
283
autofree(void)
284
{
285
   ralloc_free(autofree_context);
286
}
287
 
288
void *
289
ralloc_autofree_context(void)
290
{
291
   if (unlikely(autofree_context == NULL)) {
292
      autofree_context = ralloc_context(NULL);
293
      atexit(autofree);
294
   }
295
   return autofree_context;
296
}
297
 
298
void
299
ralloc_set_destructor(const void *ptr, void(*destructor)(void *))
300
{
301
   ralloc_header *info = get_header(ptr);
302
   info->destructor = destructor;
303
}
304
 
305
char *
306
ralloc_strdup(const void *ctx, const char *str)
307
{
308
   size_t n;
309
   char *ptr;
310
 
311
   if (unlikely(str == NULL))
312
      return NULL;
313
 
314
   n = strlen(str);
315
   ptr = ralloc_array(ctx, char, n + 1);
316
   memcpy(ptr, str, n);
317
   ptr[n] = '\0';
318
   return ptr;
319
}
320
 
321
char *
322
ralloc_strndup(const void *ctx, const char *str, size_t max)
323
{
324
   size_t n;
325
   char *ptr;
326
 
327
   if (unlikely(str == NULL))
328
      return NULL;
329
 
330
   n = strlen(str);
331
   if (n > max)
332
      n = max;
333
 
334
   ptr = ralloc_array(ctx, char, n + 1);
335
   memcpy(ptr, str, n);
336
   ptr[n] = '\0';
337
   return ptr;
338
}
339
 
340
/* helper routine for strcat/strncat - n is the exact amount to copy */
341
static bool
342
cat(char **dest, const char *str, size_t n)
343
{
344
   char *both;
345
   size_t existing_length;
346
   assert(dest != NULL && *dest != NULL);
347
 
348
   existing_length = strlen(*dest);
349
   both = resize(*dest, existing_length + n + 1);
350
   if (unlikely(both == NULL))
351
      return false;
352
 
353
   memcpy(both + existing_length, str, n);
354
   both[existing_length + n] = '\0';
355
 
356
   *dest = both;
357
   return true;
358
}
359
 
360
 
361
bool
362
ralloc_strcat(char **dest, const char *str)
363
{
364
   return cat(dest, str, strlen(str));
365
}
366
 
367
bool
368
ralloc_strncat(char **dest, const char *str, size_t n)
369
{
370
   /* Clamp n to the string length */
371
   size_t str_length = strlen(str);
372
   if (str_length < n)
373
      n = str_length;
374
 
375
   return cat(dest, str, n);
376
}
377
 
378
char *
379
ralloc_asprintf(const void *ctx, const char *fmt, ...)
380
{
381
   char *ptr;
382
   va_list args;
383
   va_start(args, fmt);
384
   ptr = ralloc_vasprintf(ctx, fmt, args);
385
   va_end(args);
386
   return ptr;
387
}
388
 
389
/* Return the length of the string that would be generated by a printf-style
390
 * format and argument list, not including the \0 byte.
391
 */
392
static size_t
393
printf_length(const char *fmt, va_list untouched_args)
394
{
395
   int size;
396
   char junk;
397
 
398
   /* Make a copy of the va_list so the original caller can still use it */
399
   va_list args;
400
   va_copy(args, untouched_args);
401
 
402
#ifdef _WIN32
403
   /* We need to use _vcsprintf to calculate the size as vsnprintf returns -1
404
    * if the number of characters to write is greater than count.
405
    */
406
   size = _vscprintf(fmt, args);
407
   (void)junk;
408
#else
409
   size = vsnprintf(&junk, 1, fmt, args);
410
#endif
411
   assert(size >= 0);
412
 
413
   va_end(args);
414
 
415
   return size;
416
}
417
 
418
char *
419
ralloc_vasprintf(const void *ctx, const char *fmt, va_list args)
420
{
421
   size_t size = printf_length(fmt, args) + 1;
422
 
423
   char *ptr = ralloc_size(ctx, size);
424
   if (ptr != NULL)
425
      vsnprintf(ptr, size, fmt, args);
426
 
427
   return ptr;
428
}
429
 
430
bool
431
ralloc_asprintf_append(char **str, const char *fmt, ...)
432
{
433
   bool success;
434
   va_list args;
435
   va_start(args, fmt);
436
   success = ralloc_vasprintf_append(str, fmt, args);
437
   va_end(args);
438
   return success;
439
}
440
 
441
bool
442
ralloc_vasprintf_append(char **str, const char *fmt, va_list args)
443
{
444
   size_t existing_length;
445
   assert(str != NULL);
446
   existing_length = *str ? strlen(*str) : 0;
447
   return ralloc_vasprintf_rewrite_tail(str, &existing_length, fmt, args);
448
}
449
 
450
bool
451
ralloc_asprintf_rewrite_tail(char **str, size_t *start, const char *fmt, ...)
452
{
453
   bool success;
454
   va_list args;
455
   va_start(args, fmt);
456
   success = ralloc_vasprintf_rewrite_tail(str, start, fmt, args);
457
   va_end(args);
458
   return success;
459
}
460
 
461
bool
462
ralloc_vasprintf_rewrite_tail(char **str, size_t *start, const char *fmt,
463
			      va_list args)
464
{
465
   size_t new_length;
466
   char *ptr;
467
 
468
   assert(str != NULL);
469
 
470
   if (unlikely(*str == NULL)) {
471
      // Assuming a NULL context is probably bad, but it's expected behavior.
472
      *str = ralloc_vasprintf(NULL, fmt, args);
473
      return true;
474
   }
475
 
476
   new_length = printf_length(fmt, args);
477
 
478
   ptr = resize(*str, *start + new_length + 1);
479
   if (unlikely(ptr == NULL))
480
      return false;
481
 
482
   vsnprintf(ptr + *start, new_length + 1, fmt, args);
483
   *str = ptr;
484
   *start += new_length;
485
   return true;
486
}