"Fossies" - the Fresh Open Source Software Archive

Member "s-nail-14.9.7/memory.c" (16 Feb 2018, 42615 Bytes) of package /linux/misc/s-nail-14.9.7.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.6_vs_14.9.7.

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