"Fossies" - the Fresh Open Source Software Archive

Member "s-nail-14.9.10/memory.c" (25 Mar 2018, 43453 Bytes) of package /linux/misc/s-nail-14.9.10.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "memory.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 14.9.9_vs_14.9.10.

    1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
    2  *@ Heap memory and automatically reclaimed storage.
    3  *@ TODO Back the _flux_ heap.
    4  *@ TODO Add cache for "the youngest" two or three n_MEMORY_AUTOREC_SIZE arenas
    5  *
    6  * Copyright (c) 2012 - 2018 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
    7  *
    8  * Permission to use, copy, modify, and/or distribute this software for any
    9  * purpose with or without fee is hereby granted, provided that the above
   10  * copyright notice and this permission notice appear in all copies.
   11  *
   12  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
   13  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
   14  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
   15  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
   16  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
   17  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
   18  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
   19  */
   20 #undef n_FILE
   21 #define n_FILE memory
   22 
   23 #ifndef HAVE_AMALGAMATION
   24 # include "nail.h"
   25 #endif
   26 
   27 /*
   28  * We use per-execution context memory arenas, to be found in
   29  * n_go_data->gdc_mempool; if NULL, set to ->gdc__mempool_buf.
   30  * n_memory_reset() that happens on loop ticks reclaims their memory, and
   31  * performs debug checks also on the former #ifdef HAVE_MEMORY_DEBUG.
   32  * The arena that is used already during program startup is special in that
   33  * _pool_fixate() will set "a lower bound" in order not to reclaim memory that
   34  * must be kept vivid during the lifetime of the program.
   35  * That was so in historical code with the globally shared single string dope
   36  * implementation, too.  (And it still seems easier than bypassing to normal
   37  * heap memory before _fixate() is called, today.)
   38  *
   39  * AutoReclaimedStorage memory is the follow-up to the historical "stringdope"
   40  * allocator from 1979 (see [timeline:a7342d9]:src/Mail/strings.c), it is
   41  * a steadily growing pool (but _relax_hold()..[:_relax_unroll():]..relax_gut()
   42  * can be used to reduce pressure) until n_memory_reset() time.
   43  *
   44  * LastOutFirstIn memory is meant as an alloca(3) replacement but which requires
   45  * lofi_free()ing pointers (otherwise growing until n_memory_reset()).
   46  *
   47  * TODO Flux heap memory is like LOFI except that any pointer can be freed (and
   48  * TODO reused) at any time, just like normal heap memory.  It is notational in
   49  * TODO that it clearly states that the allocation will go away after a loop
   50  * TODO tick, and also we can use some buffer caches.
   51  */
   52 
   53 /* If defined (and HAVE_MEMORY_DEBUG), realloc acts like alloc+free, which can
   54  * help very bogus double-free attempts */
   55 #define a_MEMORY_REALLOC_IS_ALLOC_PLUS_FREE /* TODO runtime opt <> C++ cache */
   56 
   57 /* Maximum allocation (directly) handled by A-R-Storage */
   58 #define a_MEMORY_ARS_MAX (n_MEMORY_AUTOREC_SIZE / 2 + n_MEMORY_AUTOREC_SIZE / 4)
   59 #define a_MEMORY_LOFI_MAX a_MEMORY_ARS_MAX
   60 
   61 n_CTA(a_MEMORY_ARS_MAX > 1024,
   62    "Auto-reclaimed memory requires a larger buffer size"); /* Anway > 42! */
   63 n_CTA(n_ISPOW2(n_MEMORY_AUTOREC_SIZE),
   64    "Buffers should be POW2 (may be wasteful on native allocators otherwise)");
   65 
   66 /* Alignment of ARS memory.  Simply go for pointer alignment */
   67 #define a_MEMORY_ARS_ROUNDUP(S) n_ALIGN_SMALL(S)
   68 #define a_MEMORY_LOFI_ROUNDUP(S) a_MEMORY_ARS_ROUNDUP(S)
   69 
   70 #ifdef HAVE_MEMORY_DEBUG
   71 n_CTA(sizeof(char) == sizeof(ui8_t), "But POSIX says a byte is 8 bit");
   72 
   73 # define a_MEMORY_HOPE_SIZE (2 * 8 * sizeof(char))
   74 # define a_MEMORY_HOPE_INC(P) (P) += 8
   75 # define a_MEMORY_HOPE_DEC(P) (P) -= 8
   76 
   77 /* We use address-induced canary values, inspiration (but he didn't invent)
   78  * and primes from maxv@netbsd.org, src/sys/kern/subr_kmem.c */
   79 # define a_MEMORY_HOPE_LOWER(S,P) \
   80 do{\
   81    ui64_t __h__ = (uintptr_t)(P);\
   82    __h__ *= ((ui64_t)0x9E37FFFFu << 32) | 0xFFFC0000u;\
   83    __h__ >>= 56;\
   84    (S) = (ui8_t)__h__;\
   85 }while(0)
   86 
   87 # define a_MEMORY_HOPE_UPPER(S,P) \
   88 do{\
   89    ui32_t __i__;\
   90    ui64_t __x__, __h__ = (uintptr_t)(P);\
   91    __h__ *= ((ui64_t)0x9E37FFFFu << 32) | 0xFFFC0000u;\
   92    for(__i__ = 56; __i__ != 0; __i__ -= 8)\
   93       if((__x__ = (__h__ >> __i__)) != 0){\
   94          (S) = (ui8_t)__x__;\
   95          break;\
   96       }\
   97    if(__i__ == 0)\
   98       (S) = 0xAAu;\
   99 }while(0)
  100 
  101 # define a_MEMORY_HOPE_SET(T,C) \
  102 do{\
  103    union a_memory_ptr __xp;\
  104    struct a_memory_chunk *__xc;\
  105    __xp.p_vp = (C).p_vp;\
  106    __xc = (struct a_memory_chunk*)(__xp.T - 1);\
  107    a_MEMORY_HOPE_INC((C).p_cp);\
  108    a_MEMORY_HOPE_LOWER(__xp.p_ui8p[0], &__xp.p_ui8p[0]);\
  109    a_MEMORY_HOPE_LOWER(__xp.p_ui8p[1], &__xp.p_ui8p[1]);\
  110    a_MEMORY_HOPE_LOWER(__xp.p_ui8p[2], &__xp.p_ui8p[2]);\
  111    a_MEMORY_HOPE_LOWER(__xp.p_ui8p[3], &__xp.p_ui8p[3]);\
  112    a_MEMORY_HOPE_LOWER(__xp.p_ui8p[4], &__xp.p_ui8p[4]);\
  113    a_MEMORY_HOPE_LOWER(__xp.p_ui8p[5], &__xp.p_ui8p[5]);\
  114    a_MEMORY_HOPE_LOWER(__xp.p_ui8p[6], &__xp.p_ui8p[6]);\
  115    a_MEMORY_HOPE_LOWER(__xp.p_ui8p[7], &__xp.p_ui8p[7]);\
  116    a_MEMORY_HOPE_INC(__xp.p_ui8p) + __xc->mc_user_size;\
  117    a_MEMORY_HOPE_UPPER(__xp.p_ui8p[0], &__xp.p_ui8p[0]);\
  118    a_MEMORY_HOPE_UPPER(__xp.p_ui8p[1], &__xp.p_ui8p[1]);\
  119    a_MEMORY_HOPE_UPPER(__xp.p_ui8p[2], &__xp.p_ui8p[2]);\
  120    a_MEMORY_HOPE_UPPER(__xp.p_ui8p[3], &__xp.p_ui8p[3]);\
  121    a_MEMORY_HOPE_UPPER(__xp.p_ui8p[4], &__xp.p_ui8p[4]);\
  122    a_MEMORY_HOPE_UPPER(__xp.p_ui8p[5], &__xp.p_ui8p[5]);\
  123    a_MEMORY_HOPE_UPPER(__xp.p_ui8p[6], &__xp.p_ui8p[6]);\
  124    a_MEMORY_HOPE_UPPER(__xp.p_ui8p[7], &__xp.p_ui8p[7]);\
  125 }while(0)
  126 
  127 # define a_MEMORY_HOPE_GET_TRACE(T,C,BAD) \
  128 do{\
  129    a_MEMORY_HOPE_INC((C).p_cp);\
  130    a_MEMORY_HOPE_GET(T, C, BAD);\
  131    a_MEMORY_HOPE_INC((C).p_cp);\
  132 }while(0)
  133 
  134 # define a_MEMORY_HOPE_GET(T,C,BAD) \
  135 do{\
  136    union a_memory_ptr __xp;\
  137    struct a_memory_chunk *__xc;\
  138    ui32_t __i;\
  139    ui8_t __m;\
  140    __xp.p_vp = (C).p_vp;\
  141    a_MEMORY_HOPE_DEC(__xp.p_cp);\
  142    (C).p_cp = __xp.p_cp;\
  143    __xc = (struct a_memory_chunk*)(__xp.T - 1);\
  144    (BAD) = FAL0;\
  145    __i = 0;\
  146    a_MEMORY_HOPE_LOWER(__m, &__xp.p_ui8p[0]);\
  147       if(__xp.p_ui8p[0] != __m) __i |= 1<<0;\
  148    a_MEMORY_HOPE_LOWER(__m, &__xp.p_ui8p[1]);\
  149       if(__xp.p_ui8p[1] != __m) __i |= 1<<1;\
  150    a_MEMORY_HOPE_LOWER(__m, &__xp.p_ui8p[2]);\
  151       if(__xp.p_ui8p[2] != __m) __i |= 1<<2;\
  152    a_MEMORY_HOPE_LOWER(__m, &__xp.p_ui8p[3]);\
  153       if(__xp.p_ui8p[3] != __m) __i |= 1<<3;\
  154    a_MEMORY_HOPE_LOWER(__m, &__xp.p_ui8p[4]);\
  155       if(__xp.p_ui8p[4] != __m) __i |= 1<<4;\
  156    a_MEMORY_HOPE_LOWER(__m, &__xp.p_ui8p[5]);\
  157       if(__xp.p_ui8p[5] != __m) __i |= 1<<5;\
  158    a_MEMORY_HOPE_LOWER(__m, &__xp.p_ui8p[6]);\
  159       if(__xp.p_ui8p[6] != __m) __i |= 1<<6;\
  160    a_MEMORY_HOPE_LOWER(__m, &__xp.p_ui8p[7]);\
  161       if(__xp.p_ui8p[7] != __m) __i |= 1<<7;\
  162    if(__i != 0){\
  163       (BAD) = TRU1;\
  164       a_MEMORY_HOPE_INC((C).p_cp);\
  165       n_alert("%p: corrupt lower canary: 0x%02X: %s, line %d",\
  166          (C).p_cp, __i, mdbg_file, mdbg_line);\
  167       a_MEMORY_HOPE_DEC((C).p_cp);\
  168    }\
  169    a_MEMORY_HOPE_INC(__xp.p_ui8p) + __xc->mc_user_size;\
  170    __i = 0;\
  171    a_MEMORY_HOPE_UPPER(__m, &__xp.p_ui8p[0]);\
  172       if(__xp.p_ui8p[0] != __m) __i |= 1<<0;\
  173    a_MEMORY_HOPE_UPPER(__m, &__xp.p_ui8p[1]);\
  174       if(__xp.p_ui8p[1] != __m) __i |= 1<<1;\
  175    a_MEMORY_HOPE_UPPER(__m, &__xp.p_ui8p[2]);\
  176       if(__xp.p_ui8p[2] != __m) __i |= 1<<2;\
  177    a_MEMORY_HOPE_UPPER(__m, &__xp.p_ui8p[3]);\
  178       if(__xp.p_ui8p[3] != __m) __i |= 1<<3;\
  179    a_MEMORY_HOPE_UPPER(__m, &__xp.p_ui8p[4]);\
  180       if(__xp.p_ui8p[4] != __m) __i |= 1<<4;\
  181    a_MEMORY_HOPE_UPPER(__m, &__xp.p_ui8p[5]);\
  182       if(__xp.p_ui8p[5] != __m) __i |= 1<<5;\
  183    a_MEMORY_HOPE_UPPER(__m, &__xp.p_ui8p[6]);\
  184       if(__xp.p_ui8p[6] != __m) __i |= 1<<6;\
  185    a_MEMORY_HOPE_UPPER(__m, &__xp.p_ui8p[7]);\
  186       if(__xp.p_ui8p[7] != __m) __i |= 1<<7;\
  187    if(__i != 0){\
  188       (BAD) = TRU1;\
  189       a_MEMORY_HOPE_INC((C).p_cp);\
  190       n_alert("%p: corrupt upper canary: 0x%02X: %s, line %d",\
  191          (C).p_cp, __i, mdbg_file, mdbg_line);\
  192       a_MEMORY_HOPE_DEC((C).p_cp);\
  193    }\
  194    if(BAD)\
  195       n_alert("   ..canary last seen: %s, line %u",\
  196          __xc->mc_file, __xc->mc_line);\
  197 }while(0)
  198 #endif /* HAVE_MEMORY_DEBUG */
  199 
  200 #ifdef HAVE_MEMORY_DEBUG
  201 struct a_memory_chunk{
  202    char const *mc_file;
  203    ui32_t mc_line;
  204    ui8_t mc_isfree;
  205    ui8_t mc__dummy[3];
  206    ui32_t mc_user_size;
  207    ui32_t mc_size;
  208 };
  209 
  210 /* The heap memory n_free() may become delayed to detect double frees.
  211  * It is primitive, but ok: speed and memory usage don't matter here */
  212 struct a_memory_heap_chunk{
  213    struct a_memory_chunk mhc_super;
  214    struct a_memory_heap_chunk *mhc_prev;
  215    struct a_memory_heap_chunk *mhc_next;
  216 };
  217 #endif /* HAVE_MEMORY_DEBUG */
  218 
  219 struct a_memory_ars_lofi_chunk{
  220 #ifdef HAVE_MEMORY_DEBUG
  221    struct a_memory_chunk malc_super;
  222 #endif
  223    struct a_memory_ars_lofi_chunk *malc_last; /* Bit 1 set: it's a heap alloc */
  224 };
  225 
  226 union a_memory_ptr{
  227    void *p_vp;
  228    char *p_cp;
  229    ui8_t *p_ui8p;
  230 #ifdef HAVE_MEMORY_DEBUG
  231    struct a_memory_chunk *p_c;
  232    struct a_memory_heap_chunk *p_hc;
  233 #endif
  234    struct a_memory_ars_lofi_chunk *p_alc;
  235 };
  236 
  237 struct a_memory_ars_ctx{
  238    struct a_memory_ars_ctx *mac_outer;
  239    struct a_memory_ars_buffer *mac_top;   /* Alloc stack */
  240    struct a_memory_ars_buffer *mac_full;  /* Alloc stack, cpl. filled */
  241    size_t mac_recur;                      /* _relax_create() recursion */
  242    struct a_memory_ars_huge *mac_huge;    /* Huge allocation bypass list */
  243    struct a_memory_ars_lofi *mac_lofi;    /* Pseudo alloca */
  244    struct a_memory_ars_lofi_chunk *mac_lofi_top;
  245 };
  246 n_CTA(n_MEMORY_POOL_TYPE_SIZEOF >= sizeof(struct a_memory_ars_ctx),
  247    "struct n_go_data_ctx.gdc_mempool is not large enough for memory pool");
  248 
  249 struct a_memory_ars_buffer{
  250    struct a_memory_ars_buffer *mab_last;
  251    char *mab_bot;    /* For _autorec_fixate(): keep startup memory lingering */
  252    char *mab_relax;  /* If !NULL, used by _relax_unroll() instead of .mab_bot */
  253    char *mab_caster; /* Point of casting off memory */
  254    char mab_buf[n_MEMORY_AUTOREC_SIZE - (4 * sizeof(void*))];
  255 };
  256 n_CTA(sizeof(struct a_memory_ars_buffer) == n_MEMORY_AUTOREC_SIZE,
  257    "Resulting structure size is not the expected one");
  258 #ifdef HAVE_MEMORY_DEBUG
  259 n_CTA(a_MEMORY_ARS_MAX + a_MEMORY_HOPE_SIZE + sizeof(struct a_memory_chunk)
  260       < n_SIZEOF_FIELD(struct a_memory_ars_buffer, mab_buf),
  261    "Memory layout of auto-reclaimed storage does not work out that way");
  262 #endif
  263 
  264 /* Requests that exceed a_MEMORY_ARS_MAX are always served by the normal
  265  * memory allocator (which panics if memory cannot be served).  This can be
  266  * seen as a security fallback bypass only */
  267 struct a_memory_ars_huge{
  268    struct a_memory_ars_huge *mah_last;
  269    char mah_buf[n_VFIELD_SIZE(a_MEMORY_ARS_ROUNDUP(1))];
  270 };
  271 
  272 struct a_memory_ars_lofi{
  273    struct a_memory_ars_lofi *mal_last;
  274    char *mal_caster;
  275    char *mal_max;
  276    char mal_buf[n_VFIELD_SIZE(a_MEMORY_ARS_ROUNDUP(1))];
  277 };
  278 
  279 /* */
  280 #ifdef HAVE_MEMORY_DEBUG
  281 static size_t a_memory_heap_aall, a_memory_heap_acur, a_memory_heap_amax,
  282       a_memory_heap_mall, a_memory_heap_mcur, a_memory_heap_mmax;
  283 static struct a_memory_heap_chunk *a_memory_heap_list, *a_memory_heap_free;
  284 
  285 static size_t a_memory_ars_ball, a_memory_ars_bcur, a_memory_ars_bmax,
  286       a_memory_ars_hall, a_memory_ars_hcur, a_memory_ars_hmax,
  287       a_memory_ars_aall, a_memory_ars_mall;
  288 
  289 static size_t a_memory_lofi_ball, a_memory_lofi_bcur, a_memory_lofi_bmax,
  290       a_memory_lofi_aall, a_memory_lofi_acur, a_memory_lofi_amax,
  291       a_memory_lofi_mall, a_memory_lofi_mcur, a_memory_lofi_mmax;
  292 #endif
  293 
  294 /* */
  295 n_INLINE void a_memory_lofi_free(struct a_memory_ars_ctx *macp, void *vp);
  296 
  297 /* Reset an ars_ctx */
  298 static void a_memory_ars_reset(struct a_memory_ars_ctx *macp);
  299 
  300 n_INLINE void
  301 a_memory_lofi_free(struct a_memory_ars_ctx *macp, void *vp){
  302    struct a_memory_ars_lofi *malp;
  303    union a_memory_ptr p;
  304    NYD2_ENTER;
  305 
  306    p.p_vp = vp;
  307 #ifdef HAVE_MEMORY_DEBUG
  308    --a_memory_lofi_acur;
  309    a_memory_lofi_mcur -= p.p_c->mc_user_size;
  310 #endif
  311 
  312    /* The heap allocations are released immediately */
  313    if((uintptr_t)p.p_alc->malc_last & 0x1){
  314       malp = macp->mac_lofi;
  315       macp->mac_lofi = malp->mal_last;
  316       macp->mac_lofi_top = (struct a_memory_ars_lofi_chunk*)
  317             ((uintptr_t)p.p_alc->malc_last & ~0x1);
  318       n_free(malp);
  319 #ifdef HAVE_MEMORY_DEBUG
  320       --a_memory_lofi_bcur;
  321 #endif
  322    }else{
  323       macp->mac_lofi_top = p.p_alc->malc_last;
  324 
  325       /* The normal arena ones only if the arena is empty, except for when
  326        * it is the last - that we'll keep until _pool_pop() or exit(3) */
  327       if(p.p_cp == (malp = macp->mac_lofi)->mal_buf){
  328          if(malp->mal_last != NULL){
  329             macp->mac_lofi = malp->mal_last;
  330             n_free(malp);
  331 #ifdef HAVE_MEMORY_DEBUG
  332             --a_memory_lofi_bcur;
  333 #endif
  334          }
  335       }else
  336          malp->mal_caster = p.p_cp;
  337    }
  338    NYD2_LEAVE;
  339 }
  340 
  341 static void
  342 a_memory_ars_reset(struct a_memory_ars_ctx *macp){
  343    union{
  344       struct a_memory_ars_lofi_chunk *alcp;
  345       struct a_memory_ars_lofi *alp;
  346       struct a_memory_ars_buffer *abp;
  347       struct a_memory_ars_huge *ahp;
  348    } m, m2;
  349    NYD2_ENTER;
  350 
  351    /* Simply move all buffers away from .mac_full */
  352    for(m.abp = macp->mac_full; m.abp != NULL; m.abp = m2.abp){
  353       m2.abp = m.abp->mab_last;
  354       m.abp->mab_last = macp->mac_top;
  355       macp->mac_top = m.abp;
  356    }
  357    macp->mac_full = NULL;
  358 
  359    for(m2.abp = NULL, m.abp = macp->mac_top; m.abp != NULL;){
  360       struct a_memory_ars_buffer *x;
  361 
  362       x = m.abp;
  363       m.abp = m.abp->mab_last;
  364 
  365       /* Give away all buffers that are not covered by autorec_fixate() */
  366       if(x->mab_bot == x->mab_buf){
  367          if(m2.abp == NULL)
  368             macp->mac_top = m.abp;
  369          else
  370             m2.abp->mab_last = m.abp;
  371          n_free(x);
  372 #ifdef HAVE_MEMORY_DEBUG
  373          --a_memory_ars_bcur;
  374 #endif
  375       }else{
  376          m2.abp = x;
  377          x->mab_caster = x->mab_bot;
  378          x->mab_relax = NULL;
  379 #ifdef HAVE_MEMORY_DEBUG
  380          memset(x->mab_caster, 0377,
  381             PTR2SIZE(&x->mab_buf[sizeof(x->mab_buf)] - x->mab_caster));
  382 #endif
  383       }
  384    }
  385 
  386    while((m.ahp = macp->mac_huge) != NULL){
  387       macp->mac_huge = m.ahp->mah_last;
  388       n_free(m.ahp);
  389 #ifdef HAVE_MEMORY_DEBUG
  390       --a_memory_ars_hcur;
  391 #endif
  392    }
  393 
  394    /* "alloca(3)" memory goes away, too.  XXX Must be last as long we jump */
  395 #ifdef HAVE_MEMORY_DEBUG
  396    if(macp->mac_lofi_top != NULL &&
  397          ((n_psonce & n_PSO_REPRODUCIBLE) ||
  398           (n_poption & (n_PO_DEBUG | n_PO_MEMDEBUG))))
  399       n_alert("There still is LOFI memory upon ARS reset!");
  400 #endif
  401    while((m.alcp = macp->mac_lofi_top) != NULL)
  402       a_memory_lofi_free(macp, m.alcp);
  403    NYD2_LEAVE;
  404 }
  405 
  406 FL void
  407 n_memory_reset(void){
  408 #ifdef HAVE_MEMORY_DEBUG
  409    union a_memory_ptr p;
  410    size_t c, s;
  411 #endif
  412    struct a_memory_ars_ctx *macp;
  413    NYD_ENTER;
  414 
  415    n_memory_check();
  416 
  417    if((macp = n_go_data->gdc_mempool) != NULL){
  418       /* First of all reset auto-reclaimed storage so that heap freed during
  419        * this can be handled in a second step */
  420       /* TODO v15 active recursion can only happen after a jump */
  421       if(macp->mac_recur > 0){
  422          macp->mac_recur = 1;
  423          n_autorec_relax_gut();
  424       }
  425       a_memory_ars_reset(macp);
  426    }
  427 
  428    /* Now we are ready to deal with heap */
  429 #ifdef HAVE_MEMORY_DEBUG
  430    c = s = 0;
  431 
  432    for(p.p_hc = a_memory_heap_free; p.p_hc != NULL;){
  433       void *vp;
  434 
  435       vp = p.p_hc;
  436       ++c;
  437       s += p.p_c->mc_size;
  438       p.p_hc = p.p_hc->mhc_next;
  439       (free)(vp);
  440    }
  441    a_memory_heap_free = NULL;
  442 
  443    if((n_poption & (n_PO_DEBUG | n_PO_MEMDEBUG)) && c > 0)
  444       n_err("memreset: freed %" PRIuZ " chunks/%" PRIuZ " bytes\n", c, s);
  445 #endif
  446    NYD_LEAVE;
  447 }
  448 
  449 FL void
  450 n_memory_pool_fixate(void){
  451    struct a_memory_ars_buffer *mabp;
  452    struct a_memory_ars_ctx *macp;
  453    NYD_ENTER;
  454 
  455    if((macp = n_go_data->gdc_mempool) != NULL){
  456       for(mabp = macp->mac_top; mabp != NULL; mabp = mabp->mab_last)
  457          mabp->mab_bot = mabp->mab_caster;
  458       for(mabp = macp->mac_full; mabp != NULL; mabp = mabp->mab_last)
  459          mabp->mab_bot = mabp->mab_caster;
  460    }
  461    NYD_LEAVE;
  462 }
  463 
  464 FL void
  465 n_memory_pool_push(void *vp){
  466    struct a_memory_ars_ctx *macp;
  467    NYD_ENTER;
  468 
  469    if(n_go_data->gdc_mempool == NULL)
  470       n_go_data->gdc_mempool = n_go_data->gdc__mempool_buf;
  471 
  472    memset(macp = vp, 0, sizeof *macp);
  473    macp->mac_outer = n_go_data->gdc_mempool;
  474    n_go_data->gdc_mempool = macp;
  475    NYD_LEAVE;
  476 }
  477 
  478 FL void
  479 n_memory_pool_pop(void *vp){
  480    struct a_memory_ars_buffer *mabp;
  481    struct a_memory_ars_ctx *macp;
  482    NYD_ENTER;
  483 
  484    n_memory_check();
  485 
  486    if((macp = vp) == NULL){
  487       macp = n_go_data->gdc_mempool;
  488       assert(macp != NULL);
  489    }else{
  490       /* XXX May not be ARS top upon jump */
  491       while(n_go_data->gdc_mempool != macp){
  492          DBG( n_err("ARS pop %p to reach freed context\n",
  493             n_go_data->gdc_mempool); )
  494          n_memory_pool_pop(n_go_data->gdc_mempool);
  495       }
  496    }
  497    n_go_data->gdc_mempool = macp->mac_outer;
  498 
  499    a_memory_ars_reset(macp);
  500    assert(macp->mac_full == NULL);
  501    assert(macp->mac_huge == NULL);
  502 
  503    mabp = macp->mac_top;
  504    macp->mac_top = NULL;
  505    while(mabp != NULL){
  506       vp = mabp;
  507       mabp = mabp->mab_last;
  508       n_free(vp);
  509    }
  510 
  511    /* We (may) have kept one buffer for our pseudo alloca(3) */
  512    if((vp = macp->mac_lofi) != NULL){
  513       assert(macp->mac_lofi->mal_last == NULL);
  514       macp->mac_lofi = NULL;
  515 #ifdef HAVE_MEMORY_DEBUG
  516       --a_memory_lofi_bcur;
  517 #endif
  518       n_free(vp);
  519    }
  520    NYD_LEAVE;
  521 }
  522 
  523 #ifndef HAVE_MEMORY_DEBUG
  524 FL void *
  525 n_alloc(size_t s){
  526    void *rv;
  527    NYD2_ENTER;
  528 
  529    if(s == 0)
  530       s = 1;
  531    if((rv = malloc(s)) == NULL)
  532       n_panic(_("no memory"));
  533    NYD2_LEAVE;
  534    return rv;
  535 }
  536 
  537 FL void *
  538 n_realloc(void *vp, size_t s){
  539    void *rv;
  540    NYD2_ENTER;
  541 
  542    if(vp == NULL)
  543       rv = n_alloc(s);
  544    else{
  545       if(s == 0)
  546          s = 1;
  547       if((rv = realloc(vp, s)) == NULL)
  548          n_panic(_("no memory"));
  549    }
  550    NYD2_LEAVE;
  551    return rv;
  552 }
  553 
  554 FL void *
  555 n_calloc(size_t nmemb, size_t size){
  556    void *rv;
  557    NYD2_ENTER;
  558 
  559    if(size == 0)
  560       size = 1;
  561    if((rv = calloc(nmemb, size)) == NULL)
  562       n_panic(_("no memory"));
  563    NYD2_LEAVE;
  564    return rv;
  565 }
  566 
  567 FL void
  568 (n_free)(void *vp){
  569    NYD2_ENTER;
  570    (free)(vp);
  571    NYD2_LEAVE;
  572 }
  573 
  574 #else /* !HAVE_MEMORY_DEBUG */
  575 FL void *
  576 (n_alloc)(size_t s n_MEMORY_DEBUG_ARGS){
  577    union a_memory_ptr p;
  578    ui32_t user_s;
  579    NYD2_ENTER;
  580 
  581    if(s > UI32_MAX - sizeof(struct a_memory_heap_chunk) - a_MEMORY_HOPE_SIZE)
  582       n_panic("n_alloc(): allocation too large: %s, line %d",
  583          mdbg_file, mdbg_line);
  584    if((user_s = (ui32_t)s) == 0)
  585       s = 1;
  586    s += sizeof(struct a_memory_heap_chunk) + a_MEMORY_HOPE_SIZE;
  587 
  588    if((p.p_vp = (malloc)(s)) == NULL)
  589       n_panic(_("no memory"));
  590 
  591    p.p_hc->mhc_prev = NULL;
  592    if((p.p_hc->mhc_next = a_memory_heap_list) != NULL)
  593       a_memory_heap_list->mhc_prev = p.p_hc;
  594 
  595    p.p_c->mc_file = mdbg_file;
  596    p.p_c->mc_line = (ui16_t)mdbg_line;
  597    p.p_c->mc_isfree = FAL0;
  598    p.p_c->mc_user_size = user_s;
  599    p.p_c->mc_size = (ui32_t)s;
  600 
  601    a_memory_heap_list = p.p_hc++;
  602    a_MEMORY_HOPE_SET(p_hc, p);
  603 
  604    ++a_memory_heap_aall;
  605    ++a_memory_heap_acur;
  606    a_memory_heap_amax = n_MAX(a_memory_heap_amax, a_memory_heap_acur);
  607    a_memory_heap_mall += user_s;
  608    a_memory_heap_mcur += user_s;
  609    a_memory_heap_mmax = n_MAX(a_memory_heap_mmax, a_memory_heap_mcur);
  610    NYD2_LEAVE;
  611    return p.p_vp;
  612 }
  613 
  614 FL void *
  615 (n_realloc)(void *vp, size_t s n_MEMORY_DEBUG_ARGS){
  616 # ifndef a_MEMORY_REALLOC_IS_ALLOC_PLUS_FREE
  617    ui32_t user_s;
  618 # endif
  619    bool_t isbad;
  620    union a_memory_ptr p;
  621    NYD2_ENTER;
  622 
  623    if((p.p_vp = vp) == NULL){
  624 jforce:
  625       p.p_vp = (n_alloc)(s, mdbg_file, mdbg_line);
  626       goto jleave;
  627    }
  628 
  629    a_MEMORY_HOPE_GET(p_hc, p, isbad);
  630    --p.p_hc;
  631 
  632    if(p.p_c->mc_isfree){
  633       n_err("n_realloc(): region freed!  At %s, line %d\n"
  634          "\tLast seen: %s, line %" PRIu16 "\n",
  635          mdbg_file, mdbg_line, p.p_c->mc_file, p.p_c->mc_line);
  636       goto jforce;
  637    }
  638 
  639 # ifdef a_MEMORY_REALLOC_IS_ALLOC_PLUS_FREE
  640    /* C99 */{
  641       char *xp;
  642 
  643       xp = (n_alloc)(s, mdbg_file, mdbg_line);
  644       memcpy(xp, vp, n_MIN(s, p.p_c->mc_user_size));
  645       (n_free)(vp, mdbg_file, mdbg_line);
  646       p.p_vp = xp;
  647       goto jleave;
  648    }
  649 # else
  650 
  651    if(p.p_hc == a_memory_heap_list)
  652       a_memory_heap_list = p.p_hc->mhc_next;
  653    else
  654       p.p_hc->mhc_prev->mhc_next = p.p_hc->mhc_next;
  655    if (p.p_hc->mhc_next != NULL)
  656       p.p_hc->mhc_next->mhc_prev = p.p_hc->mhc_prev;
  657 
  658    --a_memory_heap_acur;
  659    a_memory_heap_mcur -= p.p_c->mc_user_size;
  660 
  661    if(s > UI32_MAX - sizeof(struct a_memory_heap_chunk) - a_MEMORY_HOPE_SIZE)
  662       n_panic("n_realloc(): allocation too large: %s, line %d",
  663          mdbg_file, mdbg_line);
  664    if((user_s = (ui32_t)s) == 0)
  665       s = 1;
  666    s += sizeof(struct a_memory_heap_chunk) + a_MEMORY_HOPE_SIZE;
  667 
  668    if((p.p_vp = (realloc)(p.p_c, s)) == NULL)
  669       n_panic(_("no memory"));
  670    p.p_hc->mhc_prev = NULL;
  671    if((p.p_hc->mhc_next = a_memory_heap_list) != NULL)
  672       a_memory_heap_list->mhc_prev = p.p_hc;
  673 
  674    p.p_c->mc_file = mdbg_file;
  675    p.p_c->mc_line = (ui16_t)mdbg_line;
  676    p.p_c->mc_isfree = FAL0;
  677    p.p_c->mc_user_size = user_s;
  678    p.p_c->mc_size = (ui32_t)s;
  679 
  680    a_memory_heap_list = p.p_hc++;
  681    a_MEMORY_HOPE_SET(p_hc, p);
  682 
  683    ++a_memory_heap_aall;
  684    ++a_memory_heap_acur;
  685    a_memory_heap_amax = n_MAX(a_memory_heap_amax, a_memory_heap_acur);
  686    a_memory_heap_mall += user_s;
  687    a_memory_heap_mcur += user_s;
  688    a_memory_heap_mmax = n_MAX(a_memory_heap_mmax, a_memory_heap_mcur);
  689 # endif /* a_MEMORY_REALLOC_IS_ALLOC_PLUS_FREE */
  690 jleave:
  691    NYD2_LEAVE;
  692    return p.p_vp;
  693 }
  694 
  695 FL void *
  696 (n_calloc)(size_t nmemb, size_t size n_MEMORY_DEBUG_ARGS){
  697    union a_memory_ptr p;
  698    ui32_t user_s;
  699    NYD2_ENTER;
  700 
  701    if(nmemb == 0)
  702       nmemb = 1;
  703    if(size > UI32_MAX - sizeof(struct a_memory_heap_chunk) - a_MEMORY_HOPE_SIZE)
  704       n_panic("n_calloc(): allocation size too large: %s, line %d",
  705          mdbg_file, mdbg_line);
  706    if((user_s = (ui32_t)size) == 0)
  707       size = 1;
  708    if((UI32_MAX - sizeof(struct a_memory_heap_chunk) - a_MEMORY_HOPE_SIZE) /
  709          nmemb < size)
  710       n_panic("n_calloc(): allocation count too large: %s, line %d",
  711          mdbg_file, mdbg_line);
  712 
  713    size *= nmemb;
  714    size += sizeof(struct a_memory_heap_chunk) + a_MEMORY_HOPE_SIZE;
  715 
  716    if((p.p_vp = (malloc)(size)) == NULL)
  717       n_panic(_("no memory"));
  718    memset(p.p_vp, 0, size);
  719 
  720    p.p_hc->mhc_prev = NULL;
  721    if((p.p_hc->mhc_next = a_memory_heap_list) != NULL)
  722       a_memory_heap_list->mhc_prev = p.p_hc;
  723 
  724    p.p_c->mc_file = mdbg_file;
  725    p.p_c->mc_line = (ui16_t)mdbg_line;
  726    p.p_c->mc_isfree = FAL0;
  727    p.p_c->mc_user_size = (user_s > 0) ? user_s *= nmemb : 0;
  728    p.p_c->mc_size = (ui32_t)size;
  729 
  730    a_memory_heap_list = p.p_hc++;
  731    a_MEMORY_HOPE_SET(p_hc, p);
  732 
  733    ++a_memory_heap_aall;
  734    ++a_memory_heap_acur;
  735    a_memory_heap_amax = n_MAX(a_memory_heap_amax, a_memory_heap_acur);
  736    a_memory_heap_mall += user_s;
  737    a_memory_heap_mcur += user_s;
  738    a_memory_heap_mmax = n_MAX(a_memory_heap_mmax, a_memory_heap_mcur);
  739    NYD2_LEAVE;
  740    return p.p_vp;
  741 }
  742 
  743 FL void
  744 (n_free)(void *vp n_MEMORY_DEBUG_ARGS){
  745    union a_memory_ptr p;
  746    bool_t isbad;
  747    NYD2_ENTER;
  748 
  749    if((p.p_vp = vp) == NULL){
  750       n_err("n_free(NULL) from %s, line %d\n", mdbg_file, mdbg_line);
  751       goto jleave;
  752    }
  753 
  754    a_MEMORY_HOPE_GET(p_hc, p, isbad);
  755    --p.p_hc;
  756 
  757    if(p.p_c->mc_isfree){
  758       n_err("n_free(): double-free avoided at %s, line %d\n"
  759          "\tLast seen: %s, line %" PRIu16 "\n",
  760          mdbg_file, mdbg_line, p.p_c->mc_file, p.p_c->mc_line);
  761       goto jleave;
  762    }
  763 
  764    if(p.p_hc == a_memory_heap_list){
  765       if((a_memory_heap_list = p.p_hc->mhc_next) != NULL)
  766          a_memory_heap_list->mhc_prev = NULL;
  767    }else
  768       p.p_hc->mhc_prev->mhc_next = p.p_hc->mhc_next;
  769    if(p.p_hc->mhc_next != NULL)
  770       p.p_hc->mhc_next->mhc_prev = p.p_hc->mhc_prev;
  771 
  772    p.p_c->mc_file = mdbg_file;
  773    p.p_c->mc_line = (ui16_t)mdbg_line;
  774    p.p_c->mc_isfree = TRU1;
  775    /* Trash contents (also see [21c05f8]) */
  776    memset(vp, 0377, p.p_c->mc_user_size);
  777 
  778    --a_memory_heap_acur;
  779    a_memory_heap_mcur -= p.p_c->mc_user_size;
  780 
  781    if((n_psonce & n_PSO_REPRODUCIBLE) ||
  782          (n_poption & (n_PO_DEBUG | n_PO_MEMDEBUG))){
  783       p.p_hc->mhc_next = a_memory_heap_free;
  784       a_memory_heap_free = p.p_hc;
  785    }else
  786       (free)(p.p_vp);
  787 jleave:
  788    NYD2_LEAVE;
  789 }
  790 #endif /* HAVE_MEMORY_DEBUG */
  791 
  792 FL void *
  793 (n_autorec_alloc_from_pool)(void *vp, size_t size n_MEMORY_DEBUG_ARGS){
  794 #ifdef HAVE_MEMORY_DEBUG
  795    ui32_t user_s;
  796 #endif
  797    union a_memory_ptr p;
  798    union{
  799       struct a_memory_ars_buffer *abp;
  800       struct a_memory_ars_huge *ahp;
  801    } m, m2;
  802    struct a_memory_ars_ctx *macp;
  803    NYD2_ENTER;
  804 
  805    if((macp = vp) == NULL && (macp = n_go_data->gdc_mempool) == NULL)
  806       macp = n_go_data->gdc_mempool = n_go_data->gdc__mempool_buf;
  807 
  808 #ifdef HAVE_MEMORY_DEBUG
  809    user_s = (ui32_t)size;
  810 #endif
  811    if(size == 0)
  812       ++size;
  813 #ifdef HAVE_MEMORY_DEBUG
  814    size += sizeof(struct a_memory_chunk) + a_MEMORY_HOPE_SIZE;
  815 #endif
  816    size = a_MEMORY_ARS_ROUNDUP(size);
  817 
  818    /* Huge allocations are special */
  819    if(n_UNLIKELY(size > a_MEMORY_ARS_MAX)){
  820 #ifdef HAVE_MEMORY_DEBUG
  821       if(n_poption & (n_PO_DEBUG | n_PO_MEMDEBUG))
  822          n_alert("n_autorec_alloc() of %" PRIuZ " bytes from %s, line %d",
  823             size, mdbg_file, mdbg_line);
  824 #endif
  825       goto jhuge;
  826    }
  827 
  828    /* Search for a buffer with enough free space to serve request */
  829    for(m2.abp = NULL, m.abp = macp->mac_top; m.abp != NULL;
  830          m2.abp = m.abp, m.abp = m.abp->mab_last){
  831       if((p.p_cp = m.abp->mab_caster) <=
  832             &m.abp->mab_buf[sizeof(m.abp->mab_buf) - size]){
  833          /* Alignment is the one thing, the other is what is usually allocated,
  834           * and here about 40 bytes seems to be a good cut to avoid non-usable
  835           * casters.  Reown buffers supposed to be "full" to .mac_full */
  836          if(n_UNLIKELY((m.abp->mab_caster = &p.p_cp[size]) >=
  837                &m.abp->mab_buf[sizeof(m.abp->mab_buf) - 42])){
  838             if(m2.abp == NULL)
  839                macp->mac_top = m.abp->mab_last;
  840             else
  841                m2.abp->mab_last = m.abp->mab_last;
  842             m.abp->mab_last = macp->mac_full;
  843             macp->mac_full = m.abp;
  844          }
  845          goto jleave;
  846       }
  847    }
  848 
  849    /* Need a new buffer XXX "page" pool */
  850    m.abp = n_alloc(sizeof *m.abp);
  851    m.abp->mab_last = macp->mac_top;
  852    m.abp->mab_caster = &(m.abp->mab_bot = m.abp->mab_buf)[size];
  853    m.abp->mab_relax = NULL; /* Indicates allocation after _relax_create() */
  854    macp->mac_top = m.abp;
  855    p.p_cp = m.abp->mab_bot;
  856 
  857 #ifdef HAVE_MEMORY_DEBUG
  858    ++a_memory_ars_ball;
  859    ++a_memory_ars_bcur;
  860    a_memory_ars_bmax = n_MAX(a_memory_ars_bmax, a_memory_ars_bcur);
  861 #endif
  862 
  863 jleave:
  864 #ifdef HAVE_MEMORY_DEBUG
  865    p.p_c->mc_file = mdbg_file;
  866    p.p_c->mc_line = (ui16_t)mdbg_line;
  867    p.p_c->mc_user_size = user_s;
  868    p.p_c->mc_size = (ui32_t)size;
  869    ++p.p_c;
  870    a_MEMORY_HOPE_SET(p_c, p);
  871 
  872    ++a_memory_ars_aall;
  873    a_memory_ars_mall += user_s;
  874 #endif
  875    NYD2_LEAVE;
  876    return p.p_vp;
  877 
  878 jhuge:
  879    m.ahp = n_alloc(n_VSTRUCT_SIZEOF(struct a_memory_ars_huge, mah_buf) + size);
  880    m.ahp->mah_last = macp->mac_huge;
  881    macp->mac_huge = m.ahp;
  882    p.p_cp = m.ahp->mah_buf;
  883 #ifdef HAVE_MEMORY_DEBUG
  884    ++a_memory_ars_hall;
  885    ++a_memory_ars_hcur;
  886    a_memory_ars_hmax = n_MAX(a_memory_ars_hmax, a_memory_ars_hcur);
  887 #endif
  888    goto jleave;
  889 }
  890 
  891 FL void *
  892 (n_autorec_calloc_from_pool)(void *vp, size_t nmemb, size_t size
  893       n_MEMORY_DEBUG_ARGS){
  894    void *rv;
  895    NYD2_ENTER;
  896 
  897    size *= nmemb; /* XXX overflow, but only used for struct inits */
  898    rv = (n_autorec_alloc_from_pool)(vp, size n_MEMORY_DEBUG_ARGSCALL);
  899    memset(rv, 0, size);
  900    NYD2_LEAVE;
  901    return rv;
  902 }
  903 
  904 FL void
  905 n_autorec_relax_create(void){
  906    struct a_memory_ars_ctx *macp;
  907    NYD2_ENTER;
  908 
  909    if((macp = n_go_data->gdc_mempool) == NULL)
  910       macp = n_go_data->gdc_mempool = n_go_data->gdc__mempool_buf;
  911 
  912    if(macp->mac_recur++ == 0){
  913       struct a_memory_ars_buffer *mabp;
  914 
  915       for(mabp = macp->mac_top; mabp != NULL; mabp = mabp->mab_last)
  916          mabp->mab_relax = mabp->mab_caster;
  917       for(mabp = macp->mac_full; mabp != NULL; mabp = mabp->mab_last)
  918          mabp->mab_relax = mabp->mab_caster;
  919    }
  920 #ifdef HAVE_DEVEL
  921    else
  922       n_err("n_autorec_relax_create(): recursion >0\n");
  923 #endif
  924    NYD2_LEAVE;
  925 }
  926 
  927 FL void
  928 n_autorec_relax_gut(void){
  929    struct a_memory_ars_ctx *macp;
  930    NYD2_ENTER;
  931 
  932    if((macp = n_go_data->gdc_mempool) == NULL)
  933       macp = n_go_data->gdc_mempool = n_go_data->gdc__mempool_buf;
  934 
  935    assert(macp->mac_recur > 0);
  936 
  937    if(--macp->mac_recur == 0){
  938       struct a_memory_ars_buffer *mabp;
  939 
  940       macp->mac_recur = 1;
  941       n_autorec_relax_unroll();
  942       macp->mac_recur = 0;
  943 
  944       for(mabp = macp->mac_top; mabp != NULL; mabp = mabp->mab_last)
  945          mabp->mab_relax = NULL;
  946       for(mabp = macp->mac_full; mabp != NULL; mabp = mabp->mab_last)
  947          mabp->mab_relax = NULL;
  948    }
  949 #ifdef HAVE_DEVEL
  950    else
  951       n_err("n_autorec_relax_unroll(): recursion >0\n");
  952 #endif
  953    NYD2_LEAVE;
  954 }
  955 
  956 FL void
  957 n_autorec_relax_unroll(void){
  958    /* The purpose of relaxation is only that it is possible to reset the
  959     * casters, *not* to give back memory to the system.  We are presumably in
  960     * an iteration over all messages of a mailbox, and it'd be quite
  961     * counterproductive to give the system allocator a chance to waste time */
  962    struct a_memory_ars_ctx *macp;
  963    NYD2_ENTER;
  964 
  965    if((macp = n_go_data->gdc_mempool) == NULL)
  966       macp = n_go_data->gdc_mempool = n_go_data->gdc__mempool_buf;
  967 
  968    assert(macp->mac_recur > 0);
  969    n_memory_check();
  970 
  971    if(macp->mac_recur == 1){
  972       struct a_memory_ars_buffer *mabp, *x, *y;
  973 
  974       /* Buffers in the full list may become usable again! */
  975       for(x = NULL, mabp = macp->mac_full; mabp != NULL; mabp = y){
  976          y = mabp->mab_last;
  977 
  978          if(mabp->mab_relax == NULL ||
  979                mabp->mab_relax < &mabp->mab_buf[sizeof(mabp->mab_buf) - 42]){
  980             if(x == NULL)
  981                macp->mac_full = y;
  982             else
  983                x->mab_last = y;
  984             mabp->mab_last = macp->mac_top;
  985             macp->mac_top = mabp;
  986          }else
  987             x = mabp;
  988       }
  989 
  990       for(mabp = macp->mac_top; mabp != NULL; mabp = mabp->mab_last){
  991          mabp->mab_caster = (mabp->mab_relax != NULL)
  992                ? mabp->mab_relax : mabp->mab_bot;
  993 #ifdef HAVE_MEMORY_DEBUG
  994          memset(mabp->mab_caster, 0377,
  995             PTR2SIZE(&mabp->mab_buf[sizeof(mabp->mab_buf)] - mabp->mab_caster));
  996 #endif
  997       }
  998    }
  999    NYD2_LEAVE;
 1000 }
 1001 
 1002 FL void *
 1003 (n_lofi_alloc)(size_t size n_MEMORY_DEBUG_ARGS){
 1004 #ifdef HAVE_MEMORY_DEBUG
 1005    ui32_t user_s;
 1006 #endif
 1007    union a_memory_ptr p;
 1008    struct a_memory_ars_lofi *malp;
 1009    bool_t isheap;
 1010    struct a_memory_ars_ctx *macp;
 1011    NYD2_ENTER;
 1012 
 1013    if((macp = n_go_data->gdc_mempool) == NULL)
 1014       macp = n_go_data->gdc_mempool = n_go_data->gdc__mempool_buf;
 1015 
 1016 #ifdef HAVE_MEMORY_DEBUG
 1017    user_s = (ui32_t)size;
 1018 #endif
 1019    if(size == 0)
 1020       ++size;
 1021    size += sizeof(struct a_memory_ars_lofi_chunk);
 1022 #ifdef HAVE_MEMORY_DEBUG
 1023    size += a_MEMORY_HOPE_SIZE;
 1024 #endif
 1025    size = a_MEMORY_LOFI_ROUNDUP(size);
 1026 
 1027    /* Huge allocations are special */
 1028    if(n_UNLIKELY(isheap = (size > a_MEMORY_LOFI_MAX))){
 1029 #ifdef HAVE_MEMORY_DEBUG
 1030       if(n_poption & (n_PO_DEBUG | n_PO_MEMDEBUG))
 1031          n_alert("n_lofi_alloc() of %" PRIuZ " bytes from %s, line %d",
 1032             size, mdbg_file, mdbg_line);
 1033 #endif
 1034    }else if((malp = macp->mac_lofi) != NULL &&
 1035          ((p.p_cp = malp->mal_caster) <= &malp->mal_max[-size])){
 1036       malp->mal_caster = &p.p_cp[size];
 1037       goto jleave;
 1038    }
 1039 
 1040    /* Need a new buffer */
 1041    /* C99 */{
 1042       size_t i;
 1043 
 1044       i = n_VSTRUCT_SIZEOF(struct a_memory_ars_lofi, mal_buf) + size;
 1045       i = n_MAX(i, n_MEMORY_AUTOREC_SIZE);
 1046       malp = n_alloc(i);
 1047       malp->mal_last = macp->mac_lofi;
 1048       malp->mal_caster = &malp->mal_buf[size];
 1049       i -= n_VSTRUCT_SIZEOF(struct a_memory_ars_lofi, mal_buf);
 1050       malp->mal_max = &malp->mal_buf[i];
 1051       macp->mac_lofi = malp;
 1052       p.p_cp = malp->mal_buf;
 1053 
 1054 #ifdef HAVE_MEMORY_DEBUG
 1055       ++a_memory_lofi_ball;
 1056       ++a_memory_lofi_bcur;
 1057       a_memory_lofi_bmax = n_MAX(a_memory_lofi_bmax, a_memory_lofi_bcur);
 1058 #endif
 1059    }
 1060 
 1061 jleave:
 1062    p.p_alc->malc_last = macp->mac_lofi_top;
 1063    macp->mac_lofi_top = p.p_alc;
 1064    if(isheap)
 1065       p.p_alc->malc_last = (struct a_memory_ars_lofi_chunk*)
 1066             ((uintptr_t)p.p_alc->malc_last | 0x1);
 1067 
 1068 #ifndef HAVE_MEMORY_DEBUG
 1069    ++p.p_alc;
 1070 #else
 1071    p.p_c->mc_file = mdbg_file;
 1072    p.p_c->mc_line = (ui16_t)mdbg_line;
 1073    p.p_c->mc_isfree = FAL0;
 1074    p.p_c->mc_user_size = user_s;
 1075    p.p_c->mc_size = (ui32_t)size;
 1076    ++p.p_alc;
 1077    a_MEMORY_HOPE_SET(p_alc, p);
 1078 
 1079    ++a_memory_lofi_aall;
 1080    ++a_memory_lofi_acur;
 1081    a_memory_lofi_amax = n_MAX(a_memory_lofi_amax, a_memory_lofi_acur);
 1082    a_memory_lofi_mall += user_s;
 1083    a_memory_lofi_mcur += user_s;
 1084    a_memory_lofi_mmax = n_MAX(a_memory_lofi_mmax, a_memory_lofi_mcur);
 1085 #endif
 1086    NYD2_LEAVE;
 1087    return p.p_vp;
 1088 }
 1089 
 1090 FL void
 1091 (n_lofi_free)(void *vp n_MEMORY_DEBUG_ARGS){
 1092 #ifdef HAVE_MEMORY_DEBUG
 1093    bool_t isbad;
 1094 #endif
 1095    union a_memory_ptr p;
 1096    struct a_memory_ars_ctx *macp;
 1097    NYD2_ENTER;
 1098 
 1099    if((macp = n_go_data->gdc_mempool) == NULL)
 1100       macp = n_go_data->gdc_mempool = n_go_data->gdc__mempool_buf;
 1101 
 1102    if((p.p_vp = vp) == NULL){
 1103 #ifdef HAVE_MEMORY_DEBUG
 1104       n_err("n_lofi_free(NULL) from %s, line %d\n", mdbg_file, mdbg_line);
 1105 #endif
 1106       goto jleave;
 1107    }
 1108 
 1109 #ifdef HAVE_MEMORY_DEBUG
 1110    a_MEMORY_HOPE_GET(p_alc, p, isbad);
 1111    --p.p_alc;
 1112 
 1113    if(p.p_c->mc_isfree){
 1114       n_err("n_lofi_free(): double-free avoided at %s, line %d\n"
 1115          "\tLast seen: %s, line %" PRIu16 "\n",
 1116          mdbg_file, mdbg_line, p.p_c->mc_file, p.p_c->mc_line);
 1117       goto jleave;
 1118    }
 1119    p.p_c->mc_isfree = TRU1;
 1120    memset(vp, 0377, p.p_c->mc_user_size);
 1121 
 1122    if(p.p_alc != macp->mac_lofi_top){
 1123       n_err("n_lofi_free(): this is not alloca top at %s, line %d\n"
 1124          "\tLast seen: %s, line %" PRIu16 "\n",
 1125          mdbg_file, mdbg_line, p.p_c->mc_file, p.p_c->mc_line);
 1126       goto jleave;
 1127    }
 1128 
 1129    ++p.p_alc;
 1130 #endif /* HAVE_MEMORY_DEBUG */
 1131 
 1132    a_memory_lofi_free(macp, --p.p_alc);
 1133 jleave:
 1134    NYD2_LEAVE;
 1135 }
 1136 
 1137 FL void *
 1138 n_lofi_snap_create(void){ /* TODO avoid temporary alloc */
 1139    void *rv;
 1140    NYD2_ENTER;
 1141 
 1142    rv = n_lofi_alloc(1);
 1143    NYD2_LEAVE;
 1144    return rv;
 1145 }
 1146 
 1147 FL void
 1148 n_lofi_snap_unroll(void *cookie){ /* TODO optimise */
 1149    union a_memory_ptr p;
 1150    struct a_memory_ars_ctx *macp;
 1151    NYD2_ENTER;
 1152 
 1153    n_memory_check();
 1154 
 1155    if((macp = n_go_data->gdc_mempool) == NULL)
 1156       macp = n_go_data->gdc_mempool = n_go_data->gdc__mempool_buf;
 1157 
 1158    for(;;){
 1159       p.p_alc = macp->mac_lofi_top;
 1160       a_memory_lofi_free(macp, p.p_vp);
 1161       ++p.p_alc;
 1162 #ifdef HAVE_MEMORY_DEBUG
 1163       a_MEMORY_HOPE_INC(p.p_ui8p);
 1164 #endif
 1165       if(p.p_vp == cookie)
 1166          break;
 1167    }
 1168    NYD2_LEAVE;
 1169 }
 1170 
 1171 #ifdef HAVE_MEMORY_DEBUG
 1172 FL int
 1173 c_memtrace(void *vp){
 1174    /* For a_MEMORY_HOPE_GET() */
 1175    char const * const mdbg_file = "memtrace()";
 1176    int const mdbg_line = -1;
 1177    struct a_memory_ars_buffer *mabp;
 1178    struct a_memory_ars_lofi_chunk *malcp;
 1179    struct a_memory_ars_lofi *malp;
 1180    struct a_memory_ars_ctx *macp;
 1181    bool_t isbad;
 1182    union a_memory_ptr p, xp;
 1183    size_t lines;
 1184    FILE *fp;
 1185    NYD2_ENTER;
 1186 
 1187    vp = (void*)0x1;
 1188    if((fp = Ftmp(NULL, "memtr", OF_RDWR | OF_UNLINK | OF_REGISTER)) == NULL){
 1189       n_perr("tmpfile", 0);
 1190       goto jleave;
 1191    }
 1192    lines = 0;
 1193 
 1194    fprintf(fp,
 1195       "Last-Out-First-In (alloca) storage:\n"
 1196       "       Buffer cur/peek/all: %7" PRIuZ "/%7" PRIuZ "/%10" PRIuZ "\n"
 1197       "  Allocations cur/peek/all: %7" PRIuZ "/%7" PRIuZ "/%10" PRIuZ "\n"
 1198       "        Bytes cur/peek/all: %7" PRIuZ "/%7" PRIuZ "/%10" PRIuZ "\n\n",
 1199       a_memory_lofi_bcur, a_memory_lofi_bmax, a_memory_lofi_ball,
 1200       a_memory_lofi_acur, a_memory_lofi_amax, a_memory_lofi_aall,
 1201       a_memory_lofi_mcur, a_memory_lofi_mmax, a_memory_lofi_mall);
 1202    lines += 7;
 1203 
 1204    if((macp = n_go_data->gdc_mempool) == NULL)
 1205       macp = n_go_data->gdc_mempool = n_go_data->gdc__mempool_buf;
 1206    for(; macp != NULL; macp = macp->mac_outer){
 1207       fprintf(fp, "  Evaluation stack context %p (outer: %p):\n",
 1208          (void*)macp, (void*)macp->mac_outer);
 1209       ++lines;
 1210 
 1211       for(malp = macp->mac_lofi; malp != NULL;){
 1212          fprintf(fp, "    Buffer %p%s, %" PRIuZ "/%" PRIuZ " used/free:\n",
 1213             (void*)malp, ((uintptr_t)malp->mal_last & 0x1 ? " (huge)" : ""),
 1214             PTR2SIZE(malp->mal_caster - &malp->mal_buf[0]),
 1215             PTR2SIZE(malp->mal_max - malp->mal_caster));
 1216          ++lines;
 1217          malp = malp->mal_last;
 1218          malp = (struct a_memory_ars_lofi*)((uintptr_t)malp & ~1);
 1219       }
 1220 
 1221       for(malcp = macp->mac_lofi_top; malcp != NULL;){
 1222          p.p_alc = malcp;
 1223          malcp = (struct a_memory_ars_lofi_chunk*)
 1224                ((uintptr_t)malcp->malc_last & ~0x1);
 1225          xp = p;
 1226          ++xp.p_alc;
 1227          a_MEMORY_HOPE_GET_TRACE(p_alc, xp, isbad);
 1228          fprintf(fp, "      %s%p (%u bytes): %s, line %u\n",
 1229             (isbad ? "! CANARY ERROR (LOFI): " : ""), xp.p_vp,
 1230             p.p_c->mc_user_size, p.p_c->mc_file, p.p_c->mc_line);
 1231       }
 1232    }
 1233 
 1234    fprintf(fp,
 1235       "\nAuto-reclaimed storage:\n"
 1236       "           Buffers cur/peek/all: %7" PRIuZ "/%7" PRIuZ "/%10" PRIuZ "\n"
 1237       "  Huge allocations cur/peek/all: %7" PRIuZ "/%7" PRIuZ "/%10" PRIuZ "\n"
 1238       "                Allocations all: %" PRIuZ ", Bytes all: %" PRIuZ "\n\n",
 1239       a_memory_ars_bcur, a_memory_ars_bmax, a_memory_ars_ball,
 1240       a_memory_ars_hcur, a_memory_ars_hmax, a_memory_ars_hall,
 1241       a_memory_ars_aall, a_memory_ars_mall);
 1242    lines += 7;
 1243 
 1244    for(macp = n_go_data->gdc_mempool; macp != NULL; macp = macp->mac_outer){
 1245       fprintf(fp, "  Evaluation stack context %p (outer: %p):\n",
 1246          (void*)macp, (void*)macp->mac_outer);
 1247       ++lines;
 1248 
 1249       for(mabp = macp->mac_top; mabp != NULL; mabp = mabp->mab_last){
 1250          fprintf(fp, "    Buffer %p, %" PRIuZ "/%" PRIuZ " used/free:\n",
 1251             (void*)mabp,
 1252             PTR2SIZE(mabp->mab_caster - &mabp->mab_buf[0]),
 1253             PTR2SIZE(&mabp->mab_buf[sizeof(mabp->mab_buf)] - mabp->mab_caster));
 1254          ++lines;
 1255 
 1256          for(p.p_cp = mabp->mab_buf; p.p_cp < mabp->mab_caster;
 1257                ++lines, p.p_cp += p.p_c->mc_size){
 1258             xp = p;
 1259             ++xp.p_c;
 1260             a_MEMORY_HOPE_GET_TRACE(p_c, xp, isbad);
 1261             fprintf(fp, "      %s%p (%u bytes): %s, line %u\n",
 1262                (isbad ? "! CANARY ERROR (ARS, top): " : ""), xp.p_vp,
 1263                p.p_c->mc_user_size, p.p_c->mc_file, p.p_c->mc_line);
 1264          }
 1265          ++lines;
 1266       }
 1267 
 1268       for(mabp = macp->mac_full; mabp != NULL; mabp = mabp->mab_last){
 1269          fprintf(fp, "    Buffer %p, full:\n", (void*)mabp);
 1270          ++lines;
 1271 
 1272          for(p.p_cp = mabp->mab_buf; p.p_cp < mabp->mab_caster;
 1273                ++lines, p.p_cp += p.p_c->mc_size){
 1274             xp = p;
 1275             ++xp.p_c;
 1276             a_MEMORY_HOPE_GET_TRACE(p_c, xp, isbad);
 1277             fprintf(fp, "      %s%p (%u bytes): %s, line %u\n",
 1278                (isbad ? "! CANARY ERROR (ARS, full): " : ""), xp.p_vp,
 1279                p.p_c->mc_user_size, p.p_c->mc_file, p.p_c->mc_line);
 1280          }
 1281          ++lines;
 1282       }
 1283    }
 1284 
 1285    fprintf(fp,
 1286       "\nHeap memory buffers:\n"
 1287       "  Allocation cur/peek/all: %7" PRIuZ "/%7" PRIuZ "/%10" PRIuZ "\n"
 1288       "       Bytes cur/peek/all: %7" PRIuZ "/%7" PRIuZ "/%10" PRIuZ "\n\n",
 1289       a_memory_heap_acur, a_memory_heap_amax, a_memory_heap_aall,
 1290       a_memory_heap_mcur, a_memory_heap_mmax, a_memory_heap_mall);
 1291    lines += 6;
 1292 
 1293    for(p.p_hc = a_memory_heap_list; p.p_hc != NULL;
 1294          ++lines, p.p_hc = p.p_hc->mhc_next){
 1295       xp = p;
 1296       ++xp.p_hc;
 1297       a_MEMORY_HOPE_GET_TRACE(p_hc, xp, isbad);
 1298       fprintf(fp, "  %s%p (%u bytes): %s, line %u\n",
 1299          (isbad ? "! CANARY ERROR (heap): " : ""), xp.p_vp,
 1300          p.p_c->mc_user_size, p.p_c->mc_file, p.p_c->mc_line);
 1301    }
 1302 
 1303    if((n_psonce & n_PSO_REPRODUCIBLE) ||
 1304          (n_poption & (n_PO_DEBUG | n_PO_MEMDEBUG))){
 1305       fprintf(fp, "Heap buffers lingering for n_free():\n");
 1306       ++lines;
 1307 
 1308       for(p.p_hc = a_memory_heap_free; p.p_hc != NULL;
 1309             ++lines, p.p_hc = p.p_hc->mhc_next){
 1310          xp = p;
 1311          ++xp.p_hc;
 1312          a_MEMORY_HOPE_GET_TRACE(p_hc, xp, isbad);
 1313          fprintf(fp, "  %s%p (%u bytes): %s, line %u\n",
 1314             (isbad ? "! CANARY ERROR (free): " : ""), xp.p_vp,
 1315             p.p_c->mc_user_size, p.p_c->mc_file, p.p_c->mc_line);
 1316       }
 1317    }
 1318 
 1319    page_or_print(fp, lines);
 1320    Fclose(fp);
 1321    vp = NULL;
 1322 jleave:
 1323    NYD2_LEAVE;
 1324    return (vp != NULL);
 1325 }
 1326 
 1327 FL bool_t
 1328 n__memory_check(char const *mdbg_file, int mdbg_line){
 1329    union a_memory_ptr p, xp;
 1330    struct a_memory_ars_buffer *mabp;
 1331    struct a_memory_ars_lofi_chunk *malcp;
 1332    struct a_memory_ars_ctx *macp;
 1333    bool_t anybad, isbad;
 1334    NYD2_ENTER;
 1335 
 1336    anybad = FAL0;
 1337 
 1338    if((macp = n_go_data->gdc_mempool) == NULL)
 1339       macp = n_go_data->gdc_mempool = n_go_data->gdc__mempool_buf;
 1340 
 1341    /* Alloca */
 1342 
 1343    for(malcp = macp->mac_lofi_top; malcp != NULL;){
 1344       p.p_alc = malcp;
 1345       malcp = (struct a_memory_ars_lofi_chunk*)
 1346             ((uintptr_t)malcp->malc_last & ~0x1);
 1347       xp = p;
 1348       ++xp.p_alc;
 1349       a_MEMORY_HOPE_GET_TRACE(p_alc, xp, isbad);
 1350       if(isbad){
 1351          anybad = TRU1;
 1352          n_err(
 1353             "! CANARY ERROR (LOFI): %p (%u bytes): %s, line %u\n",
 1354             xp.p_vp, p.p_c->mc_user_size, p.p_c->mc_file, p.p_c->mc_line);
 1355       }
 1356    }
 1357 
 1358    /* Auto-reclaimed */
 1359 
 1360    for(mabp = macp->mac_top; mabp != NULL; mabp = mabp->mab_last){
 1361       for(p.p_cp = mabp->mab_buf; p.p_cp < mabp->mab_caster;
 1362             p.p_cp += p.p_c->mc_size){
 1363          xp = p;
 1364          ++xp.p_c;
 1365          a_MEMORY_HOPE_GET_TRACE(p_c, xp, isbad);
 1366          if(isbad){
 1367             anybad = TRU1;
 1368             n_err(
 1369                "! CANARY ERROR (ARS, top): %p (%u bytes): %s, line %u\n",
 1370                xp.p_vp, p.p_c->mc_user_size, p.p_c->mc_file, p.p_c->mc_line);
 1371          }
 1372       }
 1373    }
 1374 
 1375    for(mabp = macp->mac_full; mabp != NULL; mabp = mabp->mab_last){
 1376       for(p.p_cp = mabp->mab_buf; p.p_cp < mabp->mab_caster;
 1377             p.p_cp += p.p_c->mc_size){
 1378          xp = p;
 1379          ++xp.p_c;
 1380          a_MEMORY_HOPE_GET_TRACE(p_c, xp, isbad);
 1381          if(isbad){
 1382             anybad = TRU1;
 1383             n_err(
 1384                "! CANARY ERROR (ARS, full): %p (%u bytes): %s, line %u\n",
 1385                xp.p_vp, p.p_c->mc_user_size, p.p_c->mc_file, p.p_c->mc_line);
 1386          }
 1387       }
 1388    }
 1389 
 1390    /* Heap*/
 1391 
 1392    for(p.p_hc = a_memory_heap_list; p.p_hc != NULL; p.p_hc = p.p_hc->mhc_next){
 1393       xp = p;
 1394       ++xp.p_hc;
 1395       a_MEMORY_HOPE_GET_TRACE(p_hc, xp, isbad);
 1396       if(isbad){
 1397          anybad = TRU1;
 1398          n_err(
 1399             "! CANARY ERROR (heap): %p (%u bytes): %s, line %u\n",
 1400             xp.p_vp, p.p_c->mc_user_size, p.p_c->mc_file, p.p_c->mc_line);
 1401       }
 1402    }
 1403 
 1404    if((n_psonce & n_PSO_REPRODUCIBLE) ||
 1405          (n_poption & (n_PO_DEBUG | n_PO_MEMDEBUG))){
 1406       for(p.p_hc = a_memory_heap_free; p.p_hc != NULL;
 1407             p.p_hc = p.p_hc->mhc_next){
 1408          xp = p;
 1409          ++xp.p_hc;
 1410          a_MEMORY_HOPE_GET_TRACE(p_hc, xp, isbad);
 1411          if(isbad){
 1412             anybad = TRU1;
 1413             n_err(
 1414               "! CANARY ERROR (free): %p (%u bytes): %s, line %u\n",
 1415                xp.p_vp, p.p_c->mc_user_size, p.p_c->mc_file, p.p_c->mc_line);
 1416          }
 1417       }
 1418    }
 1419 
 1420    if(anybad && ok_blook(memdebug))
 1421       n_panic("Memory errors encountered");
 1422    NYD2_LEAVE;
 1423    return anybad;
 1424 }
 1425 #endif /* HAVE_MEMORY_DEBUG */
 1426 
 1427 /* s-it-mode */