切り出してみる。

要はこういうスタック割り当て、案外便利っすよと。

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

typedef struct Frame {
  struct Frame *prevframe;
  void *top;
  size_t index;
} Frame;
typedef struct Slot {
  void *slot;
  void *last;
  size_t size;
} Slot;
typedef struct Stack {
  Slot *slots;
  size_t slotsnum;
  Frame *lastframe;
  void *top;
  size_t index;
} Stack;

#define STACK_MINSLOTSIZE 1024
static Slot *addslot(Stack *s, size_t slotsize) {
  size_t n = s->slotsnum;
  s->slots = realloc(s->slots, (n+1)*sizeof(Slot));
  s->slots[n].slot = malloc(slotsize);
  s->slots[n].last = (void*)((char*)s->slots[n].slot + slotsize);
  s->slotsnum = n+1;
  return s->slots[n].slot;
}
static size_t stack_grow(Stack *s) {
  size_t nextindex = s->index + 1;
  size_t slotsnum = s->slotsnum;
  if (nextindex >= slotsnum) {
    int i;
    size_t newsize = 0;
    Slot *newslot;
    for(i=0;i<slotsnum;++i) {
      Slot *slot = &s->slots[i];
      newsize += (char*)slot->last - (char*) slot->slot;
    }
    addslot(s, newsize);
  }
  s->index = nextindex;
  s->top = s->slots[nextindex].slot;
  return s->index;
}
void *stack_alloc(Stack *s, size_t size) {
  void *new = s->top;
  void *newtop = (void*)((char*)new + size);
  Slot *curslot = &s->slots[s->index];
  if (newtop > curslot->last) {
    stack_grow(s);
    return stack_alloc(s, size);
  }
  s->top = newtop;
  return new;
}
Frame *stack_newframe(Stack *s) {
  void *ptop = s->top;
  size_t pindex = s->index;
  Frame *f = stack_alloc(s, sizeof(Frame));
  if (f) {
    f->prevframe = s->lastframe;
    f->top = ptop;
    f->index = pindex;
    s->lastframe = f;
  }
  return f;
}
Frame *stack_closeframe(Stack *s) {
  Frame *f = s->lastframe;
  s->top = f->top;
  s->index = f->index;
  s->lastframe = f->prevframe;
  return f;
}
Stack *stack_init(Stack *s) {
  s->slots = NULL;
  s->slotsnum = 0;
  s->lastframe = NULL;
  addslot(s, STACK_MINSLOTSIZE);
  s->index = 0;
  s->lastframe = NULL;
  s->top = s->slots[0].slot;
  s->index = 0;
  return s;
}
void stack_close(Stack *s) {
  int i;
  for(i=s->slotsnum-1;i>=0;--i) {
    free(s->slots[i].slot);
  }
  free(s->slots);
  s->slots = NULL;
}

/** stackalloc test code **/
static int *new_int(Stack *s, const int i) {
  int *dst = stack_alloc(s, sizeof(int));
  *dst = i;
  return dst;
}
typedef struct Array {
  size_t size;
  size_t length;
  char buffer[1];
} Array;
#define array_elem(a, i) ((void*)&((a)->buffer[(a)->size*(i)]))
static Array *new_array(Stack *s, size_t size, size_t len) {
  Array *a = stack_alloc(s, sizeof(Array)+size*(len-1));
  a->size = size;
  a->length = len;
  return a;
}
static Array *new_range(Stack *s, int start, int end) {
  size_t len = end - start + 1;
  Array *a = new_array(s, sizeof(int), len);
  int i;
  for(i=0;i<len;++i) {
    int *p = array_elem(a, i);
    *p = i+start;
  }
  return a;
}
void test01() {
  Stack s_;
  Stack *s = &s_;
  int i, j;
  void *p;
  stack_init(s);
  for(i=0;stack_newframe(s) && i<2;++i) {
    int *sum = new_int(s, 1);
    Array *a = new_range(s, 1, 10);
    for(j=0;stack_newframe(s) && j<10;++j) {
      int *p = array_elem(a, j);
      *sum *= *p;
      printf("%2d(%p) = %7d(%p)\n", *p, p, *sum, sum);
      stack_closeframe(s);
    }
    stack_closeframe(s);
  }
  stack_close(s);
}
int main(int argc, char **argv) {
  test01();
  return 0;
}

がこういう実行結果。

% ./stackalloc
 1(0x99f7030) =       1(0x99f7024)
 2(0x99f7034) =       2(0x99f7024)
 3(0x99f7038) =       6(0x99f7024)
 4(0x99f703c) =      24(0x99f7024)
 5(0x99f7040) =     120(0x99f7024)
 6(0x99f7044) =     720(0x99f7024)
 7(0x99f7048) =    5040(0x99f7024)
 8(0x99f704c) =   40320(0x99f7024)
 9(0x99f7050) =  362880(0x99f7024)
10(0x99f7054) = 3628800(0x99f7024)
 1(0x99f7030) =       1(0x99f7024)
 2(0x99f7034) =       2(0x99f7024)
 3(0x99f7038) =       6(0x99f7024)
 4(0x99f703c) =      24(0x99f7024)
 5(0x99f7040) =     120(0x99f7024)
 6(0x99f7044) =     720(0x99f7024)
 7(0x99f7048) =    5040(0x99f7024)
 8(0x99f704c) =   40320(0x99f7024)
 9(0x99f7050) =  362880(0x99f7024)
10(0x99f7054) = 3628800(0x99f7024)

使いどころは結構あるんじゃないかなー。論文で読んでなるほどなー思って適当に書いてるんだけど、どうなんだろう。メジャーなこういう用途のライブラリ無いのか。


スタック領域そのものをreallocせずにslotとかいう気持ち悪い単位で区切って管理してるのは、アドレス変更されたら嫌だからなんですけど、なんかもうちょいやりようあるような。1024バイトの空のslot一つだけあるときに4096バイトの領域要求したら、1024, 1024, 2048, 4096バイトという4本のslotができて、3つのslotの4096バイトになる領域が無駄になるとか。
あとWORD境界に揃えた方がいいかなーとか。
malloc捨てたりmadviceとかで性能向上とかもすんだろか。やっぱそういうライブラリ無いのか気になる。

test