Coverage Report

Created: 2026-01-25 07:18

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gettext-0.26/gettext-tools/libgettextpo/obstack.c
Line
Count
Source
1
/* obstack.c - subroutines used implicitly by object stack macros
2
   Copyright (C) 1988-2025 Free Software Foundation, Inc.
3
   This file is part of the GNU C Library.
4
5
   The GNU C Library is free software; you can redistribute it and/or
6
   modify it under the terms of the GNU Lesser General Public
7
   License as published by the Free Software Foundation; either
8
   version 2.1 of the License, or (at your option) any later version.
9
10
   The GNU C Library is distributed in the hope that it will be useful,
11
   but WITHOUT ANY WARRANTY; without even the implied warranty of
12
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13
   Lesser General Public License for more details.
14
15
   You should have received a copy of the GNU Lesser General Public
16
   License along with the GNU C Library; if not, see
17
   <https://www.gnu.org/licenses/>.  */
18
19
20
#ifdef _LIBC
21
# include <obstack.h>
22
# include <shlib-compat.h>
23
#else
24
# include <libc-config.h>
25
# include "obstack.h"
26
#endif
27
28
/* NOTE BEFORE MODIFYING THIS FILE IN GNU LIBC: _OBSTACK_INTERFACE_VERSION in
29
   gnu-versions.h must be incremented whenever callers compiled using an old
30
   obstack.h can no longer properly call the functions in this file.  */
31
32
/* If GCC, or if an oddball (testing?) host that #defines __alignof__,
33
   use the already-supplied __alignof__.  Otherwise, this must be Gnulib
34
   (as glibc assumes GCC); defer to Gnulib's alignof.  */
35
#if !defined __GNUC__ && !defined __alignof__
36
# define __alignof__(type) alignof (type)
37
#endif
38
39
#include <stdckdint.h>
40
#include <stddef.h>
41
#include <stdint.h>
42
#include <stdlib.h>
43
44
#ifndef _OBSTACK_NO_ERROR_HANDLER
45
46
/* The functions allocating more room by calling 'obstack_chunk_alloc'
47
   jump to the handler pointed to by 'obstack_alloc_failed_handler'.
48
   This can be set to a user defined function which should either
49
   abort gracefully or use longjump - but shouldn't return.  This
50
   variable by default points to the internal function
51
   'print_and_abort'.  */
52
static __attribute_noreturn__ void print_and_abort (void);
53
__attribute_noreturn__ void (*obstack_alloc_failed_handler) (void)
54
  = print_and_abort;
55
56
/* Exit value used when 'print_and_abort' is used.  */
57
# ifdef _LIBC
58
int obstack_exit_failure = EXIT_FAILURE;
59
# else
60
#  include "exitfail.h"
61
0
#  define obstack_exit_failure exit_failure
62
# endif
63
64
#endif /* !_OBSTACK_NO_ERROR_HANDLER */
65
66
#if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_3_4)
67
/* A looong time ago (before 1994, anyway; we're not sure) this global variable
68
   was used by non-GNU-C macros to avoid multiple evaluation.  The GNU C
69
   library still exports it because somebody might use it.  */
70
struct obstack *_obstack_compat = NULL;
71
compat_symbol (libc, _obstack_compat, _obstack, GLIBC_2_0);
72
#endif
73
74
/* Set *R to the least multiple of MASK + 1 that is not less than SIZE.
75
   MASK + 1 must be a power of 2.  Return true (setting *R = 0)
76
   if the result overflows, false otherwise.  */
77
static bool
78
align_chunk_size_up (_OBSTACK_CHUNK_SIZE_T *r, size_t mask,
79
                     _OBSTACK_CHUNK_SIZE_T size)
80
10.6k
{
81
10.6k
  return ckd_add (r, mask & -size, size);
82
10.6k
}
83
84
/* Call functions with either the traditional malloc/free calling
85
   interface, or the mmalloc/mfree interface (that adds an extra first
86
   argument), based on the value of use_extra_arg.  */
87
88
static void *
89
call_chunkfun (struct obstack *h, _OBSTACK_CHUNK_SIZE_T size)
90
10.6k
{
91
10.6k
  if (h->use_extra_arg)
92
0
    return h->chunkfun.extra (h->extra_arg, size);
93
10.6k
  else
94
10.6k
    return h->chunkfun.plain (size);
95
10.6k
}
96
97
static void
98
call_freefun (struct obstack *h, void *old_chunk)
99
10.6k
{
100
10.6k
  if (h->use_extra_arg)
101
0
    h->freefun.extra (h->extra_arg, old_chunk);
102
10.6k
  else
103
10.6k
    h->freefun.plain (old_chunk);
104
10.6k
}
105
106
107
/* Initialize an obstack H for use, with given CHUNK_SIZE (0 means default).
108
   Objects start on multiples of ALIGNMENT (0 means use default).
109
110
   Return nonzero if successful, calls obstack_alloc_failed_handler if
111
   allocation fails.  */
112
113
static int
114
_obstack_begin_worker (struct obstack *h,
115
                       _OBSTACK_INDEX_T chunk_size, _OBSTACK_INDEX_T alignment)
116
10.5k
{
117
10.5k
  struct _obstack_chunk *chunk; /* points to new chunk */
118
119
10.5k
  if (alignment == 0)
120
10.5k
    alignment = __alignof__ (max_align_t);
121
122
  /* The minimum size to request from the allocator, such that the
123
     result is guaranteed to have enough room to start with the struct
124
     _obstack_chunk sans contents, followed by minimal padding, up to
125
     but possibly not including the start of an aligned object.
126
     This value is zero if no size is large enough.  */
127
10.5k
  _OBSTACK_CHUNK_SIZE_T aligned_prefix_size;
128
10.5k
  bool v = align_chunk_size_up (&aligned_prefix_size, alignment - 1,
129
10.5k
                                offsetof (struct _obstack_chunk, contents));
130
131
10.5k
  _OBSTACK_CHUNK_SIZE_T size = chunk_size;
132
10.5k
  if (size < aligned_prefix_size)
133
10.5k
    {
134
10.5k
      size = aligned_prefix_size;
135
136
      /* For speed in the typical case, allocate at least a "good" size.  */
137
10.5k
      int good_size = 4000;
138
10.5k
      if (size < good_size)
139
10.5k
        size = good_size;
140
10.5k
    }
141
142
10.5k
  h->chunk_size = size;
143
10.5k
  h->alignment_mask = alignment - 1;
144
145
10.5k
  chunk = h->chunk = v ? NULL : call_chunkfun (h, size);
146
10.5k
  if (!chunk)
147
0
    (*obstack_alloc_failed_handler) ();
148
10.5k
  h->next_free = h->object_base = __PTR_ALIGN ((char *) chunk, chunk->contents,
149
10.5k
                                               alignment - 1);
150
10.5k
  h->chunk_limit = chunk->limit =
151
10.5k
    __PTR_ALIGN ((char *) chunk, (char *) chunk + size - (alignment - 1),
152
10.5k
                 alignment - 1);
153
10.5k
  chunk->prev = NULL;
154
  /* The initial chunk now contains no empty object.  */
155
10.5k
  h->maybe_empty_object = 0;
156
10.5k
  h->alloc_failed = 0;
157
10.5k
  return 1;
158
10.5k
}
159
160
int
161
_obstack_begin (struct obstack *h,
162
                _OBSTACK_INDEX_T size, _OBSTACK_INDEX_T alignment,
163
                void *(*chunkfun) (_OBSTACK_CHUNK_SIZE_T),
164
                void (*freefun) (void *))
165
10.5k
{
166
10.5k
  h->chunkfun.plain = chunkfun;
167
10.5k
  h->freefun.plain = freefun;
168
10.5k
  h->use_extra_arg = 0;
169
10.5k
  return _obstack_begin_worker (h, size, alignment);
170
10.5k
}
171
172
int
173
_obstack_begin_1 (struct obstack *h,
174
                  _OBSTACK_INDEX_T size, _OBSTACK_INDEX_T alignment,
175
                  void *(*chunkfun) (void *, _OBSTACK_CHUNK_SIZE_T),
176
                  void (*freefun) (void *, void *),
177
                  void *arg)
178
0
{
179
0
  h->chunkfun.extra = chunkfun;
180
0
  h->freefun.extra = freefun;
181
0
  h->extra_arg = arg;
182
0
  h->use_extra_arg = 1;
183
0
  return _obstack_begin_worker (h, size, alignment);
184
0
}
185
186
/* Allocate a new current chunk for the obstack *H
187
   on the assumption that LENGTH bytes need to be added
188
   to the current object, or a new object of length LENGTH allocated.
189
   Fail if LENGTH <= 0, as this means obstack_grow0's length overflowed.
190
   Copies any partial object from the end of the old chunk
191
   to the beginning of the new one.  */
192
193
void
194
_obstack_newchunk (struct obstack *h, _OBSTACK_INDEX_T length)
195
162
{
196
162
  struct _obstack_chunk *old_chunk = h->chunk;
197
162
  struct _obstack_chunk *new_chunk;
198
162
  size_t obj_size = h->next_free - h->object_base;
199
162
  char *object_base;
200
201
  /* Compute size for new chunk.  */
202
162
  _OBSTACK_CHUNK_SIZE_T s, new_size;
203
162
  bool v = length < 0;
204
162
  v |= ckd_add (&s, obj_size, length);
205
162
  v |= align_chunk_size_up (&s, h->alignment_mask, s);
206
162
  v |= ckd_add (&s, s,
207
162
                (offsetof (struct _obstack_chunk, contents)
208
162
                 + h->alignment_mask));
209
162
  if (ckd_add (&new_size, s, (obj_size >> 3) + 100))
210
0
    new_size = s;
211
162
  if (new_size < h->chunk_size)
212
62
    new_size = h->chunk_size;
213
214
  /* Allocate and initialize the new chunk.  */
215
162
  new_chunk = v ? NULL : call_chunkfun (h, new_size);
216
162
  if (!new_chunk)
217
0
    (*obstack_alloc_failed_handler)();
218
162
  h->chunk = new_chunk;
219
162
  new_chunk->prev = old_chunk;
220
162
  new_chunk->limit = h->chunk_limit =
221
162
    __PTR_ALIGN ((char *) new_chunk,
222
162
                 (char *) new_chunk + new_size - h->alignment_mask,
223
162
                 h->alignment_mask);
224
225
  /* Compute an aligned object_base in the new chunk */
226
162
  object_base =
227
162
    __PTR_ALIGN ((char *) new_chunk, new_chunk->contents, h->alignment_mask);
228
229
  /* Move the existing object to the new chunk.  */
230
162
  memcpy (object_base, h->object_base, obj_size);
231
232
  /* If the object just copied was the only data in OLD_CHUNK,
233
     free that chunk and remove it from the chain.
234
     But not if that chunk might contain an empty object.  */
235
162
  if (!h->maybe_empty_object
236
162
      && (h->object_base
237
162
          == __PTR_ALIGN ((char *) old_chunk, old_chunk->contents,
238
162
                          h->alignment_mask)))
239
32
    {
240
32
      new_chunk->prev = old_chunk->prev;
241
32
      call_freefun (h, old_chunk);
242
32
    }
243
244
162
  h->object_base = object_base;
245
162
  h->next_free = h->object_base + obj_size;
246
  /* The new chunk certainly contains no empty object yet.  */
247
162
  h->maybe_empty_object = 0;
248
162
}
249
libc_hidden_def (_obstack_newchunk)
250
251
/* Return nonzero if object OBJ has been allocated from obstack H.
252
   This is here for debugging.
253
   If you use it in a program, you are probably losing.  */
254
255
/* Suppress -Wmissing-prototypes warning.  We don't want to declare this in
256
   obstack.h because it is just for debugging.  */
257
int _obstack_allocated_p (struct obstack *h, void *obj) __attribute_pure__;
258
259
int
260
_obstack_allocated_p (struct obstack *h, void *obj)
261
0
{
262
0
  struct _obstack_chunk *lp;    /* below addr of any objects in this chunk */
263
0
  struct _obstack_chunk *plp;   /* point to previous chunk if any */
264
265
0
  lp = (h)->chunk;
266
  /* We use >= rather than > since the object cannot be exactly at
267
     the beginning of the chunk but might be an empty object exactly
268
     at the end of an adjacent chunk.  */
269
0
  while (lp != NULL && ((void *) lp >= obj || (void *) (lp)->limit < obj))
270
0
    {
271
0
      plp = lp->prev;
272
0
      lp = plp;
273
0
    }
274
0
  return lp != NULL;
275
0
}
276
277
/* Free objects in obstack H, including OBJ and everything allocate
278
   more recently than OBJ.  If OBJ is zero, free everything in H.  */
279
280
void
281
__obstack_free (struct obstack *h, void *obj)
282
10.5k
{
283
10.5k
  struct _obstack_chunk *lp;    /* below addr of any objects in this chunk */
284
10.5k
  struct _obstack_chunk *plp;   /* point to previous chunk if any */
285
286
10.5k
  lp = h->chunk;
287
  /* We use >= because there cannot be an object at the beginning of a chunk.
288
     But there can be an empty object at that address
289
     at the end of another chunk.  */
290
21.1k
  while (lp != NULL && ((void *) lp >= obj || (void *) (lp)->limit < obj))
291
10.6k
    {
292
10.6k
      plp = lp->prev;
293
10.6k
      call_freefun (h, lp);
294
10.6k
      lp = plp;
295
      /* If we switch chunks, we can't tell whether the new current
296
         chunk contains an empty object, so assume that it may.  */
297
10.6k
      h->maybe_empty_object = 1;
298
10.6k
    }
299
10.5k
  if (lp)
300
0
    {
301
0
      h->object_base = h->next_free = (char *) (obj);
302
0
      h->chunk_limit = lp->limit;
303
0
      h->chunk = lp;
304
0
    }
305
10.5k
  else if (obj != NULL)
306
    /* obj is not in any of the chunks! */
307
0
    abort ();
308
10.5k
}
309
310
/* Older versions of libc used a function _obstack_free intended to be
311
   called by non-GCC compilers.  */
312
#undef obstack_free
313
strong_alias (_obstack_free, obstack_free)
314
315
_OBSTACK_INDEX_T
316
_obstack_memory_used (struct obstack *h)
317
0
{
318
0
  struct _obstack_chunk *lp;
319
0
  _OBSTACK_INDEX_T nbytes = 0;
320
321
0
  for (lp = h->chunk; lp != NULL; lp = lp->prev)
322
0
    {
323
0
      nbytes += lp->limit - (char *) lp;
324
0
    }
325
0
  return nbytes;
326
0
}
327
328
#ifndef _OBSTACK_NO_ERROR_HANDLER
329
/* Define the error handler.  */
330
# include <stdio.h>
331
332
# ifdef _LIBC
333
#  include <libintl.h>
334
#  ifndef _
335
#   define _(msgid) gettext (msgid)
336
#  endif
337
# else
338
#  include "gettext.h"
339
#  ifndef _
340
0
#   define _(msgid) dgettext ("gnulib", msgid)
341
#  endif
342
# endif
343
344
# ifdef _LIBC
345
#  include <libio/iolibio.h>
346
# endif
347
348
static __attribute_noreturn__ void
349
print_and_abort (void)
350
0
{
351
  /* Don't change any of these strings.  Yes, it would be possible to add
352
     the newline to the string and use fputs or so.  But this must not
353
     happen because the "memory exhausted" message appears in other places
354
     like this and the translation should be reused instead of creating
355
     a very similar string which requires a separate translation.  */
356
# ifdef _LIBC
357
  (void) __fxprintf (NULL, "%s\n", _("memory exhausted"));
358
# else
359
0
  fprintf (stderr, "%s\n", _("memory exhausted"));
360
0
# endif
361
0
  exit (obstack_exit_failure);
362
0
}
363
#endif /* !_OBSTACK_NO_ERROR_HANDLER */