summaryrefslogtreecommitdiff
path: root/include/ruby/internal/xmalloc.h
blob: 57552e4e7decf00309dfdb1ea0fd0673ccde98c9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
#ifndef RBIMPL_XMALLOC_H                             /*-*-C++-*-vi:se ft=cpp:*/
#define RBIMPL_XMALLOC_H
/**
 * @file
 * @author     Ruby developers <ruby-core@ruby-lang.org>
 * @copyright  This  file  is   a  part  of  the   programming  language  Ruby.
 *             Permission  is hereby  granted,  to  either redistribute  and/or
 *             modify this file, provided that  the conditions mentioned in the
 *             file COPYING are met.  Consult the file for details.
 * @warning    Symbols   prefixed  with   either  `RBIMPL`   or  `rbimpl`   are
 *             implementation details.   Don't take  them as canon.  They could
 *             rapidly appear then vanish.  The name (path) of this header file
 *             is also an  implementation detail.  Do not expect  it to persist
 *             at the place it is now.  Developers are free to move it anywhere
 *             anytime at will.
 * @note       To  ruby-core:  remember  that   this  header  can  be  possibly
 *             recursively included  from extension  libraries written  in C++.
 *             Do not  expect for  instance `__VA_ARGS__` is  always available.
 *             We assume C99  for ruby itself but we don't  assume languages of
 *             extension libraries.  They could be written in C++98.
 * @brief      Declares ::ruby_xmalloc().
 */
#include "ruby/internal/config.h"

#ifdef STDC_HEADERS
# include <stddef.h>
#endif

#ifdef HAVE_STDLIB_H
# include <stdlib.h>
#endif

#include "ruby/internal/attr/alloc_size.h"
#include "ruby/internal/attr/nodiscard.h"
#include "ruby/internal/attr/noexcept.h"
#include "ruby/internal/attr/restrict.h"
#include "ruby/internal/attr/returns_nonnull.h"
#include "ruby/internal/dllexport.h"

/**
 * @private
 * @warning  Do not touch this macro.
 * @warning  It is an implementation detail.
 * @warning  It was a failure at the first place to let you know about it.
 * @warning  The  value of  this  macro  must match  for  ruby  itself and  all
 *           extension  libraries, otherwise  serious  memory corruption  shall
 *           occur.
 */
#ifndef USE_GC_MALLOC_OBJ_INFO_DETAILS
# define USE_GC_MALLOC_OBJ_INFO_DETAILS 0
#endif

#define xmalloc   ruby_xmalloc   /**< @old{ruby_xmalloc} */
#define xmalloc2  ruby_xmalloc2  /**< @old{ruby_xmalloc2} */
#define xcalloc   ruby_xcalloc   /**< @old{ruby_xcalloc} */
#define xrealloc  ruby_xrealloc  /**< @old{ruby_xrealloc} */
#define xrealloc2 ruby_xrealloc2 /**< @old{ruby_xrealloc2} */
#define xfree     ruby_xfree     /**< @old{ruby_xfree} */

RBIMPL_SYMBOL_EXPORT_BEGIN()

RBIMPL_ATTR_NODISCARD()
RBIMPL_ATTR_RESTRICT()
RBIMPL_ATTR_RETURNS_NONNULL()
RBIMPL_ATTR_ALLOC_SIZE((1))
/**
 * Allocates a  storage instance.  It is  largely the same as  system malloc(),
 * except:
 *
 *   - It raises Ruby exceptions instead of returning NULL, and
 *   - In case of `ENOMEM` it tries to GC to make some room.
 *
 * @param[in]  size            Requested amount of memory.
 * @exception  rb_eNoMemError  No space left for `size` bytes allocation.
 * @return     A valid pointer  to an allocated storage instance;  which has at
 *             least `size` bytes width, with appropriate alignment detected by
 *             the underlying malloc() routine.
 * @note       It doesn't return NULL.
 * @note       Unlike some malloc() implementations, it allocates something and
 *             returns a meaningful value even when `size` is equal to zero.
 * @warning    The return  value shall  be invalidated  exactly once  by either
 *             ruby_xfree(),  ruby_xrealloc(), or  ruby_xrealloc2().   It is  a
 *             failure to pass it to system free(), because the system and Ruby
 *             might or might not share the same malloc() implementation.
 */
void *ruby_xmalloc(size_t size)
RBIMPL_ATTR_NOEXCEPT(malloc(size))
;

RBIMPL_ATTR_NODISCARD()
RBIMPL_ATTR_RESTRICT()
RBIMPL_ATTR_RETURNS_NONNULL()
RBIMPL_ATTR_ALLOC_SIZE((1,2))
/**
 * Identical to ruby_xmalloc(), except it allocates `nelems` * `elemsiz` bytes.
 * This is needed  because the multiplication could integer  overflow.  On such
 * situations  Ruby does  not try  to  allocate at  all but  raises Ruby  level
 * exceptions  instead.  If  there  is  no integer  overflow  the behaviour  is
 * exactly the same as `ruby_xmalloc(nelems*elemsiz)`.
 *
 * @param[in]  nelems          Number of elements.
 * @param[in]  elemsiz         Size of an element.
 * @exception  rb_eNoMemError  No space left for allocation.
 * @exception  rb_eArgError    `nelems` * `elemsiz` would overflow.
 * @return     A valid pointer  to an allocated storage instance;  which has at
 *             least  `nelems`  *  `elemsiz`   bytes  width,  with  appropriate
 *             alignment detected by the underlying malloc() routine.
 * @note       It doesn't return NULL.
 * @note       Unlike some malloc() implementations, it allocates something and
 *             returns a  meaningful value even  when `nelems` or  `elemsiz` or
 *             both are zero.
 * @warning    The return  value shall  be invalidated  exactly once  by either
 *             ruby_xfree(),  ruby_xrealloc(), or  ruby_xrealloc2().   It is  a
 *             failure to pass it to system free(), because the system and Ruby
 *             might or might not share the same malloc() implementation.
 */
void *ruby_xmalloc2(size_t nelems, size_t elemsiz)
RBIMPL_ATTR_NOEXCEPT(malloc(nelems * elemsiz))
;

RBIMPL_ATTR_NODISCARD()
RBIMPL_ATTR_RESTRICT()
RBIMPL_ATTR_RETURNS_NONNULL()
RBIMPL_ATTR_ALLOC_SIZE((1,2))
/**
 * Identical  to  ruby_xmalloc2(),  except  it returns  a  zero-filled  storage
 * instance.  It  can also be  seen as  a routine identical  to ruby_xmalloc(),
 * except it calls calloc() instead of malloc().
 *
 * @param[in]  nelems          Number of elements.
 * @param[in]  elemsiz         Size of an element.
 * @exception  rb_eNoMemError  No space left for allocation.
 * @exception  rb_eArgError    `nelems` * `elemsiz` would overflow.
 * @return     A valid pointer  to an allocated storage instance;  which has at
 *             least  `nelems`  *  `elemsiz`   bytes  width,  with  appropriate
 *             alignment detected by the underlying calloc() routine.
 * @post       The returned storage instance is filled with zeros.
 * @note       It doesn't return NULL.
 * @note       Unlike some calloc() implementations, it allocates something and
 *             returns a  meaningful value even  when `nelems` or  `elemsiz` or
 *             both are zero.
 * @warning    The return  value shall  be invalidated  exactly once  by either
 *             ruby_xfree(),  ruby_xrealloc(), or  ruby_xrealloc2().   It is  a
 *             failure to pass it to system free(), because the system and Ruby
 *             might or might not share the same malloc() implementation.
 */
void *ruby_xcalloc(size_t nelems, size_t elemsiz)
RBIMPL_ATTR_NOEXCEPT(calloc(nelems, elemsiz))
;

RBIMPL_ATTR_NODISCARD()
RBIMPL_ATTR_RETURNS_NONNULL()
RBIMPL_ATTR_ALLOC_SIZE((2))
/**
 * Resize the storage instance.
 *
 * @param[in]  ptr             A valid  pointer to a storage  instance that was
 *                             previously returned from either:
 *                               - ruby_xmalloc(),
 *                               - ruby_xmalloc2(),
 *                               - ruby_xcalloc(),
 *                               - ruby_xrealloc(), or
 *                               - ruby_xrealloc2().
 * @param[in]  newsiz          Requested new amount of memory.
 * @exception  rb_eNoMemError  No space left for `newsiz` bytes allocation.
 * @return     A  valid  pointer  to   a  (possibly  newly  allocated)  storage
 *             instance;  which  has  at   least  `newsiz`  bytes  width,  with
 *             appropriate  alignment  detected  by  the  underlying  realloc()
 *             routine.
 * @pre        The passed pointer must point  to a valid live storage instance.
 *             It is a failure to pass an already freed pointer.
 * @post       In  case the  function  returns the  passed  pointer as-is,  the
 *             storage  instance that  the  pointer holds  is  either grown  or
 *             shrunken  to have  at least  `newsiz` bytes.  Otherwise a  valid
 *             pointer to a  newly allocated storage instance  is returned.  In
 *             this  case  `ptr`  is  invalidated   as  if  it  was  passed  to
 *             ruby_xfree().
 * @note       It doesn't return NULL.
 * @warning    Unlike some realloc() implementations,  passing zero to `newsiz`
 *             is not the  same as calling ruby_xfree(),  because this function
 *             never returns NULL.  Something meaningful still returns then.
 * @warning    It is  a failure not to  check the return value.   Do not assume
 *             anything on  it.  It could  be either identical to,  or distinct
 *             form the passed argument.
 * @warning    Do not  assume anything  on the alignment  of the  return value.
 *             There is  no guarantee  that it  inherits the  passed argument's
 *             one.
 * @warning    The return  value shall  be invalidated  exactly once  by either
 *             ruby_xfree(),  ruby_xrealloc(), or  ruby_xrealloc2().   It is  a
 *             failure to pass it to system free(), because the system and Ruby
 *             might or might not share the same malloc() implementation.
 */
void *ruby_xrealloc(void *ptr, size_t newsiz)
RBIMPL_ATTR_NOEXCEPT(realloc(ptr, newsiz))
;

RBIMPL_ATTR_NODISCARD()
RBIMPL_ATTR_RETURNS_NONNULL()
RBIMPL_ATTR_ALLOC_SIZE((2,3))
/**
 * Identical to ruby_xrealloc(),  except it resizes the  given storage instance
 * to `newelems` *  `newsiz` bytes.  This is needed  because the multiplication
 * could integer overflow.   On such situations Ruby does not  try to touch the
 * contents  of  argument pointer  at  all  but  raises Ruby  level  exceptions
 * instead.  If there is no integer  overflow the behaviour is exactly the same
 * as `ruby_xrealloc(ptr,nelems*elemsiz)`.
 *
 * This  is   roughly  the  same   as  reallocarray()  function   that  OpenBSD
 * etc. provides, but also interacts with our GC.
 *
 * @param[in]  ptr             A valid  pointer to a storage  instance that was
 *                             previously returned from either:
 *                               - ruby_xmalloc(),
 *                               - ruby_xmalloc2(),
 *                               - ruby_xcalloc(),
 *                               - ruby_xrealloc(), or
 *                               - ruby_xrealloc2().
 * @param[in]  newelems        Requested new number of elements.
 * @param[in]  newsiz          Requested new size of each element.
 * @exception  rb_eNoMemError  No space left for  allocation.
 * @exception  rb_eArgError    `newelems` * `newsiz` would overflow.
 * @return     A  valid  pointer  to   a  (possibly  newly  allocated)  storage
 *             instance; which has at least  `newelems` * `newsiz` bytes width,
 *             with appropriate alignment detected  by the underlying realloc()
 *             routine.
 * @pre        The passed pointer must point  to a valid live storage instance.
 *             It is a failure to pass an already freed pointer.
 * @post       In  case the  function  returns the  passed  pointer as-is,  the
 *             storage  instance that  the  pointer holds  is  either grown  or
 *             shrunken  to   have  at  least  `newelems`   *  `newsiz`  bytes.
 *             Otherwise a valid pointer to  a newly allocated storage instance
 *             is returned.   In this case  `ptr` is  invalidated as if  it was
 *             passed to ruby_xfree().
 * @note       It doesn't return NULL.
 * @warning    Unlike some  realloc() implementations,  passing zero  to either
 *             `newelems`   or  `elemsiz`   are   not  the   same  as   calling
 *             ruby_xfree(),   because  this   function  never   returns  NULL.
 *             Something meaningful still returns then.
 * @warning    It is  a failure not to  check the return value.   Do not assume
 *             anything on  it.  It could  be either identical to,  or distinct
 *             form the passed argument.
 * @warning    Do not  assume anything  on the alignment  of the  return value.
 *             There is  no guarantee  that it  inherits the  passed argument's
 *             one.
 * @warning    The return  value shall  be invalidated  exactly once  by either
 *             ruby_xfree(),  ruby_xrealloc(), or  ruby_xrealloc2().   It is  a
 *             failure to pass it to system free(), because the system and Ruby
 *             might or might not share the same malloc() implementation.
 */
void *ruby_xrealloc2(void *ptr, size_t newelems, size_t newsiz)
RBIMPL_ATTR_NOEXCEPT(realloc(ptr, newelems * newsiz))
;

/**
 * Deallocates a storage instance.
 *
 * @param[out]  ptr  Either
 *                     - NULL, or
 *                     - a valid pointer previously returned from one of:
 *                       - ruby_xmalloc(),
 *                       - ruby_xmalloc2(),
 *                       - ruby_xcalloc(),
 *                       - ruby_xrealloc(), or
 *                       - ruby_xrealloc2().
 * @pre         The passed pointer must point to a valid live storage instance.
 *              It is a failure to pass an already freed pointer.
 * @post        The  storage  instance  pointed  by  the  passed  pointer  gets
 *              invalidated; it is no longer addressable.
 * @warning     Every single storage instance  that was previously allocated by
 *              either    ruby_xmalloc(),   ruby_xmalloc2(),    ruby_xcalloc(),
 *              ruby_xrealloc(),  or  ruby_xrealloc2()   shall  be  invalidated
 *              exactly once by  either passing it to  ruby_xfree(), or passing
 *              it to  either ruby_xrealloc(), ruby_xrealloc2() then  check the
 *              return value for invalidation.
 * @warning     Do not pass anything other  than pointers described above.  For
 *              instance pointers returned from malloc() or mmap() shall not be
 *              passed  to   this  function,  because  the   underlying  memory
 *              management mechanism could differ.
 * @warning     Do  not pass  any invalid  pointers  to this  function e.g.  by
 *              calling it twice with a same argument.
 */
void ruby_xfree(void *ptr)
RBIMPL_ATTR_NOEXCEPT(free(ptr))
;

#if USE_GC_MALLOC_OBJ_INFO_DETAILS
# define ruby_xmalloc(s1)            ruby_xmalloc_with_location(s1, __FILE__, __LINE__)
# define ruby_xmalloc2(s1, s2)       ruby_xmalloc2_with_location(s1, s2, __FILE__, __LINE__)
# define ruby_xcalloc(s1, s2)        ruby_xcalloc_with_location(s1, s2, __FILE__, __LINE__)
# define ruby_xrealloc(ptr, s1)      ruby_xrealloc_with_location(ptr, s1, __FILE__, __LINE__)
# define ruby_xrealloc2(ptr, s1, s2) ruby_xrealloc2_with_location(ptr, s1, s2, __FILE__, __LINE__)

RBIMPL_ATTR_NODISCARD()
RBIMPL_ATTR_RESTRICT()
RBIMPL_ATTR_RETURNS_NONNULL()
RBIMPL_ATTR_ALLOC_SIZE((1))
void *ruby_xmalloc_body(size_t size)
RBIMPL_ATTR_NOEXCEPT(malloc(size))
;

RBIMPL_ATTR_NODISCARD()
RBIMPL_ATTR_RESTRICT()
RBIMPL_ATTR_RETURNS_NONNULL()
RBIMPL_ATTR_ALLOC_SIZE((1,2))
void *ruby_xmalloc2_body(size_t nelems, size_t elemsiz)
RBIMPL_ATTR_NOEXCEPT(malloc(nelems * elemsiz))
;

RBIMPL_ATTR_NODISCARD()
RBIMPL_ATTR_RESTRICT()
RBIMPL_ATTR_RETURNS_NONNULL()
RBIMPL_ATTR_ALLOC_SIZE((1,2))
void *ruby_xcalloc_body(size_t nelems, size_t elemsiz)
RBIMPL_ATTR_NOEXCEPT(calloc(nelems, elemsiz))
;

RBIMPL_ATTR_NODISCARD()
RBIMPL_ATTR_RETURNS_NONNULL()
RBIMPL_ATTR_ALLOC_SIZE((2))
void *ruby_xrealloc_body(void *ptr, size_t newsiz)
RBIMPL_ATTR_NOEXCEPT(realloc(ptr, newsiz))
;

RBIMPL_ATTR_NODISCARD()
RBIMPL_ATTR_RETURNS_NONNULL()
RBIMPL_ATTR_ALLOC_SIZE((2,3))
void *ruby_xrealloc2_body(void *ptr, size_t newelems, size_t newsiz)
RBIMPL_ATTR_NOEXCEPT(realloc(ptr, newelems * newsiz))
;

RUBY_EXTERN const char *ruby_malloc_info_file;
RUBY_EXTERN int ruby_malloc_info_line;

static inline void *
ruby_xmalloc_with_location(size_t s, const char *file, int line)
{
    void *ptr;
    ruby_malloc_info_file = file;
    ruby_malloc_info_line = line;
    ptr = ruby_xmalloc_body(s);
    ruby_malloc_info_file = NULL;
    return ptr;
}

static inline void *
ruby_xmalloc2_with_location(size_t s1, size_t s2, const char *file, int line)
{
    void *ptr;
    ruby_malloc_info_file = file;
    ruby_malloc_info_line = line;
    ptr = ruby_xmalloc2_body(s1, s2);
    ruby_malloc_info_file = NULL;
    return ptr;
}

static inline void *
ruby_xcalloc_with_location(size_t s1, size_t s2, const char *file, int line)
{
    void *ptr;
    ruby_malloc_info_file = file;
    ruby_malloc_info_line = line;
    ptr = ruby_xcalloc_body(s1, s2);
    ruby_malloc_info_file = NULL;
    return ptr;
}

static inline void *
ruby_xrealloc_with_location(void *ptr, size_t s, const char *file, int line)
{
    void *rptr;
    ruby_malloc_info_file = file;
    ruby_malloc_info_line = line;
    rptr = ruby_xrealloc_body(ptr, s);
    ruby_malloc_info_file = NULL;
    return rptr;
}

static inline void *
ruby_xrealloc2_with_location(void *ptr, size_t s1, size_t s2, const char *file, int line)
{
    void *rptr;
    ruby_malloc_info_file = file;
    ruby_malloc_info_line = line;
    rptr = ruby_xrealloc2_body(ptr, s1, s2);
    ruby_malloc_info_file = NULL;
    return rptr;
}
#endif

RBIMPL_SYMBOL_EXPORT_END()

#endif /* RBIMPL_XMALLOC_H */