1 /* NetHack 3.7 alloc.c $NHDT-Date: 1737281026 2025/01/19 02:03:46 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.38 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /*-Copyright (c) Robert Patrick Rankin, 2012. */
4 /* NetHack may be freely redistributed. See license for details. */
6 #define ALLOC_C /* comment line for pre-compiled headers */
7 /* since this file is also used in auxiliary programs, don't include all the
8 function declarations for all of nethack */
9 #define EXTERN_H /* comment line for pre-compiled headers */
17 /*#define FITSint(x) FITSint_(x, __func__, __LINE__)*/
18 extern int FITSint_(LUA_INTEGER
, const char *, int) NONNULLARG2
;
19 /*#define FITSuint(x) FITSuint_(x, __func__, __LINE__)*/
20 extern unsigned FITSuint_(unsigned long long, const char *, int) NONNULLARG2
;
22 char *fmt_ptr(const genericptr
) NONNULL
;
28 extern void free(genericptr_t
);
29 staticfn
void heapmon_init(void);
31 static FILE *heaplog
= 0;
32 static boolean tried_heaplog
= FALSE
;
36 * For historical reasons, nethack's alloc() returns 'long *' rather
37 * than 'void *' or 'char *'.
39 * Some static analysis complains if it can't deduce that the number
40 * of bytes being allocated is a multiple of 'sizeof (long)'. It
41 * recognizes that the following manipulation overcomes that via
42 * rounding the requested length up to the next long. NetHack doesn't
43 * make a lot of tiny allocations, so this shouldn't waste much memory
44 * regardless of whether malloc() does something similar. NetHack
45 * isn't expected to call alloc(0), but if that happens treat it as
46 * alloc(sizeof (long)) instead.
48 #define ForceAlignedLength(LTH) \
50 if (!(LTH) || (LTH) % sizeof (long) != 0) \
51 (LTH) += sizeof (long) - (LTH) % sizeof (long); \
55 long *alloc(unsigned int) NONNULL
;
56 long *re_alloc(long *, unsigned int) NONNULL
;
58 /* for #if MONITOR_HEAP, alloc() might return Null but only nhalloc()
59 should be calling it; nhalloc() never returns Null */
60 long *alloc(unsigned int);
61 long *re_alloc(long *, unsigned int);
62 long *nhalloc(unsigned int, const char *, int) NONNULL
;
63 long *nhrealloc(long *, unsigned int, const char *, int) NONNULL
;
65 ATTRNORETURN
extern void panic(const char *, ...) PRINTF_F(1, 2) NORETURN
;
68 alloc(unsigned int lth
)
72 ForceAlignedLength(lth
);
76 panic("Memory allocation failure; cannot get %u bytes", lth
);
78 /* for #if MONITOR_HEAP, failure is handled in nhalloc() */
83 /* realloc() call that might get substituted by nhrealloc(p,n,file,line) */
85 re_alloc(long *oldptr
, unsigned int newlth
)
89 ForceAlignedLength(newlth
);
90 newptr
= (long *) realloc((genericptr_t
) oldptr
, (size_t) newlth
);
92 /* "extend to": assume it won't ever fail if asked to shrink */
93 if (newlth
&& !newptr
)
94 panic("Memory allocation failure; cannot extend to %u bytes", newlth
);
96 /* for #if MONITOR_HEAP, failure is handled in nhrealloc() */
103 #define PTR_TYP genericptr_t
105 #define PTR_FMT "%06lx"
106 #define PTR_TYP unsigned long
109 /* A small pool of static formatting buffers.
110 * PTRBUFSIZ: We assume that pointers will be formatted as integers in
111 * hexadecimal, requiring at least 16+1 characters for each buffer to handle
112 * 64-bit systems, but the standard doesn't mandate that encoding and an
113 * implementation could do something different for %p, so we make some
115 * PTRBUFCNT: Number of formatted values which can be in use at the same
116 * time. To have more, callers need to make copies of them as they go.
120 static char ptrbuf
[PTRBUFCNT
][PTRBUFSIZ
];
121 static int ptrbufidx
= 0;
123 /* format a pointer for display purposes; returns a static buffer */
125 fmt_ptr(const genericptr ptr
)
129 buf
= ptrbuf
[ptrbufidx
];
130 if (++ptrbufidx
>= PTRBUFCNT
)
133 Sprintf(buf
, PTR_FMT
, (PTR_TYP
) ptr
);
139 /* If ${NH_HEAPLOG} is defined and we can create a file by that name,
140 then we'll log the allocation and release information to that file. */
144 char *logname
= getenv("NH_HEAPLOG");
146 if (logname
&& *logname
)
147 heaplog
= fopen(logname
, "w");
148 tried_heaplog
= TRUE
;
152 nhalloc(unsigned int lth
, const char *file
, int line
)
154 long *ptr
= alloc(lth
);
159 (void) fprintf(heaplog
, "+%5u %s %4d %s\n", lth
,
160 fmt_ptr((genericptr_t
) ptr
), line
, file
);
161 /* potential panic in alloc() was deferred til here */
163 panic("Cannot get %u bytes, line %d of %s", lth
, line
, file
);
168 /* re_alloc() with heap logging; we lack access to the old alloc size */
176 long *newptr
= re_alloc(oldptr
, newlth
);
181 char op
= '*'; /* assume realloc() will change size of previous
182 * allocation rather than make a new one */
184 if (newptr
!= oldptr
) {
185 /* if oldptr wasn't Null, realloc() freed it */
187 (void) fprintf(heaplog
, "%c%5s %s %4d %s\n", '<', "",
188 fmt_ptr((genericptr_t
) oldptr
), line
, file
);
189 op
= '>'; /* new allocation rather than size-change of old one */
191 (void) fprintf(heaplog
, "%c%5u %s %4d %s\n", op
, newlth
,
192 fmt_ptr((genericptr_t
) newptr
), line
, file
);
194 /* potential panic in re_alloc() was deferred til here;
195 "extend to": assume it won't ever fail if asked to shrink;
196 even if that assumption happens to be wrong, we lack access to
197 the old size so can't use alternate phrasing for that case */
198 if (newlth
&& !newptr
)
199 panic("Cannot extend to %u bytes, line %d of %s", newlth
, line
, file
);
205 nhfree(genericptr_t ptr
, const char *file
, int line
)
210 (void) fprintf(heaplog
, "- %s %4d %s\n",
211 fmt_ptr((genericptr_t
) ptr
), line
, file
);
216 /* strdup() which uses our alloc() rather than libc's malloc(),
217 with caller tracking */
219 nhdupstr(const char *string
, const char *file
, int line
)
221 /* we've got some info about the caller, so use it instead of __func__ */
222 unsigned len
= FITSuint_(strlen(string
), file
, line
);
224 if (FITSuint(len
+ 1, file
, line
) < len
)
225 panic("nhdupstr: string length overflow, line %d of %s",
228 return strcpy((char *) nhalloc(len
+ 1, file
, line
), string
);
232 #endif /* MONITOR_HEAP */
234 /* strdup() which uses our alloc() rather than libc's malloc();
235 not used when MONITOR_HEAP is enabled, but included unconditionally
236 in case utility programs get built using a different setting for that */
238 dupstr(const char *string
)
240 size_t len
= strlen(string
);
242 /* make sure len+1 doesn't overflow plain unsigned (for alloc()) */
243 if (len
> (unsigned) (~0U - 1U))
244 panic("dupstr: string length overflow");
246 return strcpy((char *) alloc(len
+ 1), string
);
249 /* similar for reasonable size strings, but return length of input as well */
251 dupstr_n(const char *string
, unsigned int *lenout
)
253 size_t len
= strlen(string
);
255 if (len
>= LARGEST_INT
)
256 panic("dupstr_n: string too long");
257 *lenout
= (unsigned int) len
;
258 return strcpy((char *) alloc(len
+ 1), string
);
262 /* cast to int or panic on overflow; use via macro */
264 FITSint_(LUA_INTEGER i
, const char *file
, int line
)
269 panic("Overflow at %s:%d", file
, line
);
274 FITSuint_(unsigned long long ull
, const char *file
, int line
)
276 unsigned uret
= (unsigned) ull
;
279 panic("Overflow at %s:%d", file
, line
);