diff --git a/include/extern.h b/include/extern.h index f6a2d8982..48a410337 100644 --- a/include/extern.h +++ b/include/extern.h @@ -1736,6 +1736,7 @@ extern void wake_nearby(void); extern void wake_nearto(coordxy, coordxy, int); extern void seemimic(struct monst *) NONNULLARG1; extern void normal_shape(struct monst *) NONNULLARG1; +extern void alloc_itermonarr(unsigned); extern void iter_mons_safe(boolean (*)(struct monst *)); extern void iter_mons(void (*)(struct monst *)); extern struct monst *get_iter_mons(boolean (*)(struct monst *)); diff --git a/src/mon.c b/src/mon.c index 3a66cc41d..ecdbfd3a7 100644 --- a/src/mon.c +++ b/src/mon.c @@ -4102,6 +4102,34 @@ normal_shape(struct monst *mon) } } +/* freed by freedynamicdata() when game ends; doesn't need to be struct g */ +static struct monst **itermonarr = NULL; +static unsigned itermonsiz = 0; /* size in 'monst *' pointers */ + +/* manage itermonarr; it used to be allocated and freed every time the + monster movement loop ran; now, keep it around most of the time */ +void +alloc_itermonarr(unsigned count) +{ + /* if count is 0 or bigger than itemarrsiz or much smaller than + itemarrsiz, release itermonarr (add reset itermarrsiz to 0) */ + if (!count || count > itermonsiz || count + 40 < itermonsiz) { + if (itermonarr) + free((genericptr_t) itermonarr), itermonarr = NULL; + itermonsiz = 0; + } + /* when count is more than itermonsiz (including when that just + got reset to 0), allocate a new instance of itermonarr; + implies that count is greater than 0 */ + if (count > itermonsiz) { + /* overallocate to reduce free/alloc-again thrashing when the + number of monsters varies from turn to turn */ + itermonsiz = count + 20; + itermonarr = (struct monst **) alloc( + itermonsiz * sizeof (struct monst *)); + } +} + /* Iterate all monsters on the level, even dead or off-map ones, calling bfunc() for each monster. If bfunc() returns TRUE, stop iterating. If the game ends during the call to bfunc(), then freedynamicdata() @@ -4112,27 +4140,24 @@ normal_shape(struct monst *mon) void iter_mons_safe(boolean (*bfunc)(struct monst *)) { - struct monst **monarr, *mtmp; - int i, nmons; + struct monst *mtmp; + unsigned i, nmons; for (nmons = 0, mtmp = fmon; mtmp; mtmp = mtmp->nmon) nmons++; - if (nmons) { - monarr = (struct monst **) alloc(nmons * sizeof (struct monst *)); - gi.itermonarr = monarr; /* accessible to freedynamicdata() */ + /* make sure itermonarr[] is big enough to hold nmons entries */ + alloc_itermonarr(nmons); + if (nmons) { for (i = 0, mtmp = fmon; mtmp; mtmp = mtmp->nmon) - monarr[i++] = mtmp; + itermonarr[i++] = mtmp; for (i = 0; i < nmons; i++) { - mtmp = monarr[i]; + mtmp = itermonarr[i]; if ((*bfunc)(mtmp)) break; } - - free(monarr); - gi.itermonarr = NULL; } return; } diff --git a/src/save.c b/src/save.c index fd8c76f39..9d832b73d 100644 --- a/src/save.c +++ b/src/save.c @@ -1184,8 +1184,7 @@ freedynamicdata(void) /* move-specific data */ dmonsfree(); /* release dead monsters */ - if (gi.itermonarr) - free((genericptr_t) gi.itermonarr), gi.itermonarr = NULL; + alloc_itermonarr(0U); /* a request of 0 releases existing allocation */ /* level-specific data */ done_object_cleanup(); /* maybe force some OBJ_FREE items onto map */