/* Copyright 1992, 1995, 1997 J"orn Rennecke */
#include <sys/mman.h>
#ifdef linux
#include <linux/mman.h>
#define MAP_ANON MAP_ANONYMOUS
#ifndef MAP_FILE
#define MAP_FILE 0
#endif
#endif
#include "common.h"
#include "alloc.h"
#include "object.h"
#include "uid.h"
#include "schedule.h"
int out_of_memory;
union svalue reserved_user_area, reserved_master_area, reserved_system_area;
int malloc_privilege;
size_t pagesize;
uint8 *smallpnt, *smallend;
union svalue ref_inc(union svalue sv) {
switch((freeable_type)SV_TYPE(sv)) {
case T_STRING:
case T_LSTRING:
/* make_string_global() decrements ref to sv and inc. ref to return val. */
return make_string_global(sv);
case T_ISTRING:
SV_REF(sv)--;
sv = SV_ISTRING(sv);
if (!SV_REFINC(sv))
break;
case T_GSTRING:
SV_REF(sv) = 0x100 - SV_REF_CYCLELEN;
if (!++SV_STRREF(sv))
SV_STRREF(sv)--;
break;
case T_ILSTRING:
SV_REF(sv)--;
sv = SV_ISTRING(sv);
if (!SV_REFINC(sv))
break;
case T_GLSTRING:
SV_REF(sv) = 0x100 - SV_REF_CYCLELEN;
SV_LSTRREF(sv)++;
break;
default:
fatal("bogus ref_inc\n");
return SV_NULL;
case T_MAPPING:
{
union svalue sv2;
SV_REF(sv) = 0x100 - SV_REF_CYCLELEN;
if (MAP_HAS_X(&SV_MAPPING(sv))) {
MAP_REF(&SV_MAPPING(sv))++;
break;
}
sv2 = ALLOC_TTS(T_INTERNAL, IT_X_MAP, 2,
sizeof(char *)+ sizeof(struct map_x));
if (!sv2.i)
return sv2;
SV_MAPPING(sv).x.x = (struct map_x *)&sv2.p[-1];
break;
}
case T_ARRAY:
{
svalue xsv;
struct array_x *x;
SV_REF(sv) = 0x100 - SV_REF_CYCLELEN;
xsv = ALLOC(T_INTERNAL, IT_X_ARRAY, sizeof *x);
if (!xsv.p) {
SV_REF(sv)--;
sv = TO_SVALUE(&nil_array); /* garbage in garbage out */
if (!SV_REFINC(sv))
break;
} else {
p_int len;
x = (struct array_x *)(void *)&xsv.p[3];
#if 0 /* gcc 2.7.0 bug cases extra register to be pushed */
x->len = SV_ARRAY(sv).len;
x->uid = SV_ARRAY(sv).x.uid;
#else
len = SV_ARRAY(sv).len;
x->uid = SV_ARRAY(sv).x.uid;
x->len = len;
#endif
SV_LARRAY_REF(sv) = 1;
SV_ARRAY(sv).x.x = x;
break;
}
}
case T_LARRAY:
SV_REF(sv) = 0x100 - SV_REF_CYCLELEN;
SV_LARRAY_REF(sv)++;
break;
case T_CBR_CHAR_LVALUE:
{
union svalue sv2;
SV_REF(sv)--;
sv2 = *SV_LVALUE(sv).lvalue;
if (sv2.p != SV_LVALUE(sv).parent.p || !SV_IS_2REF_STRING(sv2)) {
sv = SV_NULL;
break;
}
sv.i = *SV_LVALUE(sv).index2.p << 1;
break;
}
case T_FLOAT:
{
union svalue sv2;
SV_REF(sv)--;
sv2 = ALLOC_FLOAT;
if (sv2.p) { /* nomem -> garbage in garbage out */
SV_FLOAT(sv2) = SV_FLOAT(sv);
}
break;
}
}
return sv;
}
typedef int balance_t; /* make this the fastest signed integer type */
typedef unsigned ubalance_t; /* and this the matching unsigned type */
struct free_block {
p_uint size;
struct free_block *parent, *left, *right;
balance_t balance;
};
static void free_large_block (uint8 *sv, p_uint size);
void remove_from_free_tree(struct free_block *p);
void add_to_free_tree(struct free_block *r, p_uint size);
static union svalue alloc_large_block(p_int type, p_uint size);
static union svalue alloc_small_block(p_int type, p_uint size);
#define SMALL_BLOCK_MAX 9
#define SMALL_BLOCK_CMAX (SMALL_BLOCK_MAX * sizeof(char *))
#define ALLOC_OVERHEAD (sizeof(char *))
#define SV_NEXTFREE(sv) (*(void **)(void *)&(sv)[-1])
#define SV_FREEBLOCK(sv) (*(struct free_block *)(void *)&(sv)[sizeof(p_int)-1])
#define SV_PREVFREE(sv) (((struct free_block **)(sv-1))[-1])
#define SIZE_P_INDEX(base, size) \
(*(void **)(void *)((char *)base + size - ALLOC_OVERHEAD))
#define SIZE_I_INDEX(base, size) \
(*(p_int *)(void *)((char *)base + size - ALLOC_OVERHEAD))
static uint8 *sftable[SMALL_BLOCK_MAX],
*sfmtable[SMALL_BLOCK_MAX], *sfstable[SMALL_BLOCK_MAX];
INLINE void free_block(uint8 *sv, mp_int size) {
if (size <= SMALL_BLOCK_CMAX) {
SIZE_I_INDEX(adtstat+ALLOC_FREE1, size)++;
SV_NEXTFREE(sv) = SIZE_P_INDEX(sftable, size);
SIZE_P_INDEX(sftable, size) = sv;
return;
} else {
free_large_block(sv, size);
}
}
void init_alloc() {
char *heap, *small;
pagesize = getpagesize();
heap =
mmap((caddr_t)0, MAX_ALLOCED,
PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0);
if (heap == (caddr_t)-1) switch(errno) {
default:
perror("mmap");
fatal("mmap failed\n");
}
((p_int *)&heap[MAX_ALLOCED])[-1] = C2PI(T_INTERNAL, IT_HEAPEND, 0, 0);
add_to_free_tree(
(struct free_block *)(heap+sizeof(p_int)),
MAX_ALLOCED - sizeof(p_int)
);
small =
mmap((caddr_t)0, MAX_SMALL_ALLOCED,
PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0);
if (small == (caddr_t)-1) switch(errno) {
default:
perror("mmap");
fatal("mmap failed\n");
}
smallpnt = small;
smallend = small + MAX_SMALL_ALLOCED;
}
void free_gen(void * p) {
free_block(
(uint8 *)p - sizeof(char *) + 1,
SV_GENLEN((union svalue)((uint8 *)p - sizeof(char *) + 1))
);
}
struct searchstr {
struct searchstr *next;
char block[4];
};
static RISC_INLINE void _free_array(union svalue sv, p_int offset, p_int size)
{
union svalue *svp, *end, sv2;
svp = (union svalue *)(sv.p + offset);
end = svp + size;
if (svp < end) {
do {
sv2 = *svp++;
FREE_SVALUE(sv2);
} while (svp < end);
}
free_block(sv.p, (uint8 *)end - sv.p + 1);
}
struct searchlstr {
struct searchlstr *next;
uint32 len;
char block[4];
};
void _free_svalue(union svalue sv) {
p_int size;
switch((freeable_type)SV_TYPE(sv)) {
case T_OBJECT:
/* This can only happen if the total ref has exceeded the minor ref
* before.
*/
SV_REF(sv) = SV_REF_CYCLELEN;
O_REF(&SV_OBJECT(sv))--;
return;
case T_DESTRUCTED:
if (O_HAS_X(&SV_OBJECT(sv))) {
SV_REF(sv) = SV_REF_CYCLELEN;
if (--O_REF(&SV_OBJECT(sv)))
return;
}
_free_object(sv);
return;
case T_LONG:
size = sizeof (char *) + sizeof SV_LONG(sv);
break;
case T_FLOAT:
size = sizeof (char *) + sizeof SV_FLOAT(sv);
break;
case T_VARARGS:
free_varargs(sv);
return;
default:
fatal("bogus free_svalue\n");
return;
case T_CLOSURE:
{
union svalue ob = SV_CLOSURE(sv).g.ob;
switch(SV_CLOSURE(sv).g.closure_type) {
case CLOSURE_ALIEN_LFUN:
case CLOSURE_BOGUS_ALIEN:
FREE_ALLOCED_SVALUE(ob);
ob = SV_CLOSURE(sv).alien.alien;
FREE_ALLOCED_SVALUE(ob);
size = sizeof SV_CLOSURE(sv).alien;
break;
case CLOSURE_LFUN:
case CLOSURE_IDENTIFIER:
case CLOSURE_BOGUS_LFUN:
FREE_ALLOCED_SVALUE(ob);
size = sizeof SV_CLOSURE(sv).lfun;
break;
case CLOSURE_BOUND_LAMBDA:
{
struct lambda_closure *l2;
l2 = SV_CLOSURE(sv).bound.lambda;
if (--l2->ref) {
/* (g)cc should merge this code with the lfun closure code */
FREE_ALLOCED_SVALUE(ob);
size = sizeof SV_CLOSURE(sv).bound;
break;
}
free_block(sv.p, sizeof SV_CLOSURE(sv).bound);
sv = TO_SVALUE(l2);
}
case CLOSURE_LAMBDA:
case CLOSURE_BOGUS_LAMBDA:
FREE_ALLOCED_SVALUE(ob);
case CLOSURE_UNBOUND_LAMBDA:
_free_lambda_closure(sv);
return;
/* case CLOSURE_BOGUS */
default:
FREE_ALLOCED_SVALUE(ob);
size = sizeof SV_CLOSURE(sv).efun;
break;
}
break;
}
case T_MAPPING:
SV_REF(sv) = SV_REF_CYCLELEN;
if (MAP_HAS_X(&SV_MAPPING(sv)) && --MAP_REF(&SV_MAPPING(sv)))
return;
_free_mapping(sv);
return;
case T_LARRAY:
{
struct array_x *x;
struct uid *uid;
SV_REF(sv) = SV_REF_CYCLELEN;
if (--SV_LARRAY_REF(sv))
return;
x = SV_ARRAY(sv).x.x;
#if 0 /* gcc 2.7.0 problem: allocates %esi */
free_block((uint8 *)x - sizeof (char *) + 1,
sizeof(struct array_x));
size = x->len;
uid = x->uid;
#else
x = (struct array_x *)((uint8 *)x - sizeof (char *) + 1);
free_block((uint8 *)x, sizeof(struct array_x));
size = ((struct array_x *)((uint8 *)x - sizeof (char *) + 1))->len;
uid = ((struct array_x *)((uint8 *)x - sizeof (char *) + 1))->uid;
#endif
goto got_size;
case T_ARRAY:
size = SV_ARRAY(sv).len;
uid = SV_ARRAY(sv).x.uid;
got_size:
uid->num_array--;
uid->total_array -= size;
_free_array(sv, offsetof(struct array, member) - 1, size);
return;
}
case T_GLSTRING:
{
struct searchlstr **search, *curr, *prev;
SV_REF(sv) = SV_REF_CYCLELEN;
if (--SV_LSTRREF(sv))
return;
adtstat[SHS_LFREE]++;
search = &SV_LSTRNXT(sv);
#if defined(i386) /* save a register push */
sv.p = (uint8 *)search;
#define search ((struct searchlstr **)sv.p)
#endif
curr = *search;
do {
prev = curr;
curr = curr->next;
} while(&curr->next != search);
prev->next = curr->next;
#ifdef search
sv.p = (uint8 *)search - sizeof(char *) + 1;
#undef search
#endif
goto free_lstring;
#if 0
free_large_block(sv.p, -sizeof(char *) & sizeof(char *)*2 - 1 +
sizeof(SV_LSTRNXT(sv)) + sizeof(SV_LSTRLEN(sv)) + SV_LSTRLEN(sv));
return;
#endif
}
case T_ILSTRING:
{
union svalue sv2 = SV_ILSTRING(sv);
FREE_ALLOCED_SVALUE(sv2);
}
case T_LSTRING:
free_lstring:
free_large_block(sv.p, -sizeof(char *) & sizeof(char *)*2 - 1 +
sizeof(SV_ILSTRING(sv)) + sizeof(SV_LSTRLEN(sv)) + SV_LSTRLEN(sv));
return;
case T_GSTRING:
{
struct searchstr **search, *curr, *prev;
SV_REF(sv) = SV_REF_CYCLELEN;
if (--SV_STRREF(sv) < 254 || ++SV_STRREF(sv))
return;
adtstat[SHS_FREE]++;
search = &SV_STRNXT(sv);
#if defined(i386) /* save a register push */
sv.p = (uint8 *)search;
#define search ((struct searchstr **)sv.p)
#endif
curr = *search;
do {
prev = curr;
curr = curr->next;
} while(&curr->next != search);
prev->next = curr->next;
#ifdef search
sv.p = (uint8 *)search - sizeof(char *) + 1;
#endif
size = -sizeof(char *) & sizeof(char *)*2 - 1 +
sizeof(SV_STRNXT(sv)) + SV_STRLEN(sv);
break;
}
case T_ISTRING:
{
union svalue sv2 = SV_ISTRING(sv);
FREE_ALLOCED_SVALUE(sv2);
}
case T_STRING:
size = -sizeof(char *) & sizeof(char *)*2 - 1 +
sizeof(SV_ISTRING(sv)) + SV_STRLEN(sv);
break;
}
free_block(sv.p, size);
}
static void free_large_block (uint8 *sv, p_uint size) {
uint8 type;
struct free_block *fb;
adtstat[ALLOC_LFREE]++;
adtstat[ALLOC_LFREE_TOTAL] += size;
type = sv[size-1] & T_MASK;
if (SVTYPE_IS_FREE(type)) {
if (type & 1) {
struct free_block *nxt;
fb = &SV_FREEBLOCK(sv);
nxt = (struct free_block *)((char *)fb + size);
size += nxt->size;
remove_from_free_tree(nxt);
goto got_fb;
} else {
size += sv[size];
}
}
fb = &SV_FREEBLOCK(sv);
got_fb:
if (((uint8 *)fb)[-sizeof(char *)] & AL_PREV_FREE) {
p_int prev_size;
if ( !(prev_size = ((uint8 *)fb)[1-2*sizeof(char *)]) ) {
prev_size = ((p_int *)fb)[-3];
size += prev_size;
fb = (struct free_block *)((char *)fb - prev_size);
remove_from_free_tree(fb);
} else {
size += prev_size;
fb = (struct free_block *)((char *)fb - prev_size);
}
}
((uint8 *)fb)[size-sizeof(char *)] |= AL_PREV_FREE;
add_to_free_tree(fb, size);
}
union svalue alloc(p_int type, p_uint len) {
if (len <= SMALL_BLOCK_CMAX) {
union svalue sv;
SIZE_I_INDEX(adtstat+ALLOC_ALLOC1, len)++;
sv = SIZE_P_INDEX(sftable, len);
if (sv.i) {
SIZE_P_INDEX(sftable, len) = SV_NEXTFREE(sv.p);
*(p_int *)&sv.p[-1] = type;
return sv;
}
return alloc_small_block(type, len);
} else {
return alloc_large_block(type, len);
}
}
void *alloc_gen(mp_int len) {
union svalue r;
len += sizeof(char *);
r = ALLOC_TTS(T_INTERNAL, IT_GENERIC, len, len);
if (r.i)
r.p += 3;
return r.p;
};
static union svalue alloc_small_block(p_int type, p_uint len) {
/* len <= SMALL_BLOCK_CMAX */
union svalue sv;
uint8 *newend;
sv.p = smallpnt;
newend = sv.p + pagesize;
if (newend > smallend) {
newend = smallend;
}
newend -= len;
if (sv.p <= newend) {
sv.p++;
newend++;
do {
SV_NEXTFREE(sv.p) = SIZE_P_INDEX(sftable, len);
SIZE_P_INDEX(sftable, len) = sv.p;
sv.p += len;
} while (sv.p <= newend);
smallpnt = sv.p - 1;
return alloc(type, len);
}
if (malloc_privilege >= ALLOC_MASTER) {
sv = SIZE_P_INDEX(sfmtable, len);
if (sv.i) {
SIZE_P_INDEX(sfmtable, len) = SV_NEXTFREE(sv.p);
*(p_int *)&sv.p[-1] = type;
return sv;
}
}
if (malloc_privilege >= ALLOC_SYSTEM) {
sv = SIZE_P_INDEX(sfstable, len);
if (sv.i) {
SIZE_P_INDEX(sfmtable, len) = SV_NEXTFREE(sv.p);
*(p_int *)&sv.p[-1] = type;
return sv;
}
}
SIZE_I_INDEX(adtstat+ALLOC_ALLOC1, len)--;
return SV_NULL;
}
/* prepare two nodes for the free tree that will never be removed,
so that we can always assume that the tree is and remains non-empty. */
/* some compilers don't understand forward declarations of static vars. */
extern struct free_block dummy2;
static struct free_block dummy =
{ /*size*/0, /*parent*/&dummy2, /*left*/0, /*right*/0, /*balance*/0 };
struct free_block dummy2 =
{ /*size*/0, /*parent*/0, /*left*/&dummy, /*right*/0, /*balance*/-1 };
static struct free_block *free_tree = &dummy2;
void remove_from_free_tree(struct free_block *p) {
struct free_block *q, *r, *s, *t;
if (p->left) {
if (q = p->right) {
/* two childs */
s = q;
for ( ; r = q, q = r->left; );
if (r == s) {
r->left = s = p->left;
s->parent = r;
if (r->parent = s = p->parent) {
if (p == s->left) {
s->left = r;
} else {
s->right = r;
}
} else {
free_tree = r;
}
r->balance = p->balance;
p = r;
goto balance_right;
} else {
t = r->parent;
if (t->left = s = r->right) {
s->parent = t;
}
r->balance = p->balance;
r->left = s = p->left;
s->parent = r;
r->right = s = p->right;
s->parent = r;
if (r->parent = s = p->parent) {
if (p == s->left) {
s->left = r;
} else {
s->right = r;
}
} else {
free_tree = r;
}
p = t;
goto balance_left;
}
} else /* no right child, but left child */ {
/* We set up the free list in a way so that there will remain at
least two nodes, and the avl property ensures that the left
child is a leaf ==> there is a parent */
s = p;
p = s->parent;
r = s->left;
r->parent = p;
if (s == p->left) {
p->left = r;
goto balance_left;
} else {
p->right = r;
goto balance_right;
}
}
} else /* no left child */ {
/* We set up the free list in a way so that there is a node left
of all used nodes, so there is a parent */
s = p;
p = s->parent;
if(q = r = s->right) {
r->parent = p;
}
if (s == p->left) {
p->left = r;
goto balance_left;
} else {
p->right = r;
goto balance_right;
}
}
balance_q:
r = p;
p = q;
if (r == p->right) {
balance_t b;
balance_right:
b = p->balance;
if (b > 0) {
p->balance = 0;
if (q = p->parent) goto balance_q;
return;
} else if (b < 0) {
r = p->left;
b = r->balance;
if (b <= 0) {
/* R-Rotation */
if (p->left = s = r->right) {
s->parent = p;
}
r->right = p;
s = p->parent;
p->parent = r;
b += 1;
r->balance = b;
b = -b;
if (r->parent = s) {
if (p->balance = b) {
if (p == s->left) {
s->left = r;
return;
} else {
s->right = r;
return;
}
}
if (p == s->left) {
/* left from parent */
goto balance_left_s;
} else {
/* right from parent */
p = s;
p->right = r;
goto balance_right;
}
}
p->balance = b;
free_tree = r;
return;
} else /* r->balance == +1 */ {
/* LR-Rotation */
balance_t b2;
t = r->right;
b = t->balance;
if (p->left = s = t->right) {
s->parent = p;
}
if (r->right = s = t->left ) {
s->parent = r;
}
t->left = r;
t->right = p;
r->parent = t;
s = p->parent;
p->parent = t;
#ifdef NO_BARREL_SHIFT
b = -b;
b2 = b >> 1;
r->balance = b2;
b -= b2;
p->balance = b;
#else
b2 = (ubalance_t)b >> (8 * sizeof(b2) - 1);
p->balance = b2;
b2 = -b2 -b;
r->balance = b2;
#endif
t->balance = 0;
if (t->parent = s) {
if (p == s->left) {
p = s;
s->left = t;
goto balance_left;
} else {
p = s;
s->right = t;
goto balance_right;
}
}
free_tree = t;
return;
}
} else /* p->balance == 0 */ {
p->balance = -1;
return;
}
} else /* r == p->left */ {
balance_t b;
goto balance_left;
balance_left_s:
p = s;
s->left = r;
balance_left:
b = p->balance;
if (b < 0) {
p->balance = 0;
if (q = p->parent) goto balance_q;
return;
} else if (b > 0) {
r = p->right;
b = r->balance;
if (b >= 0) {
/* L-Rotation */
if (p->right = s = r->left) {
s->parent = p;
}
r->left = p;
s = p->parent;
p->parent = r;
b -= 1;
r->balance = b;
b = -b;
if (r->parent = s) {
if (p->balance = b) {
if (p == s->left) {
s->left = r;
return;
} else {
s->right = r;
return;
}
}
if (p == s->left) {
/* left from parent */
goto balance_left_s;
} else {
/* right from parent */
p = s;
p->right = r;
goto balance_right;
}
}
p->balance = b;
free_tree = r;
return;
} else /* r->balance == -1 */ {
/* RL-Rotation */
balance_t b2;
t = r->left;
b = t->balance;
if (p->right = s = t->left ) {
s->parent = p;
}
if (r->left = s = t->right) {
s->parent = r;
}
t->right = r;
t->left = p;
r->parent = t;
s = p->parent;
p->parent = t;
#ifdef NO_BARREL_SHIFT
b = -b;
b2 = b >> 1;
p->balance = b2;
b -= b2;
r->balance = b;
#else
b2 = (ubalance_t)b >> (8 * sizeof(b2) - 1);
r->balance = b2;
b2 = -b2 -b;
p->balance = b2;
#endif
t->balance = 0;
if (t->parent = s) {
if (p == s->left) {
p = s;
s->left = t;
goto balance_left;
} else {
s->right = t;
p = s;
goto balance_right;
}
}
free_tree = t;
return;
}
} else /* p->balance == 0 */ {
p->balance++;
return;
}
}
}
void add_to_free_tree(struct free_block *r, p_uint size) {
struct free_block *p, *q;
/* When there is a distinction between data and address registers and/or
accesses, gcc will choose data type for q, so an assignment to q will
faciliate branching
*/
((uint8 *)r)[-sizeof(char *)] = T_LARGEFREE;
*(p_int*)&((uint8 *)r)[size - 3*sizeof(char *)] = size;
((uint8 *)r)[size - 2*sizeof(char *)+1] = 0;
q = (struct free_block *)size; /* this assignment is a hint for register
choice */
q = free_tree;
for ( ; ; /*p = q*/) {
p = (struct free_block *)q;
if (size < p->size) {
if (q = p->left) {
continue;
}
/* add left */
p->left = r;
break;
} else /* >= */ {
if (q = p->right) {
continue;
}
/* add right */
p->right = r;
break;
}
}
r->size = size;
r->parent = p;
r->left = 0;
r->right = 0;
r->balance = 0;
do {
struct free_block *s;
if (r == p->left) {
balance_t b;
if ( !(b = p->balance) ) {
/* growth propagation from left side */
p->balance = -1;
} else if (b < 0) {
if (r->balance < 0) {
/* R-Rotation */
if (p->left = s = r->right) {
s->parent = p;
}
r->right = p;
p->balance = 0;
r->balance = 0;
s = p->parent;
p->parent = r;
if (r->parent = s) {
if ( s->left == p) {
s->left = r;
} else {
s->right = r;
}
} else {
free_tree = r;
}
} else /* r->balance == +1 */ {
/* LR-Rotation */
balance_t b2;
struct free_block *t = r->right;
if (p->left = s = t->right) {
s->parent = p;
}
/* relocated right subtree */
t->right = p;
if (r->right = s = t->left ) {
s->parent = r;
}
/* relocated left subtree */
t->left = r;
b = t->balance;
#ifdef NO_BARREL_SHIFT
b = -b;
b2 = b >> 1;
r->balance = b2;
b -= b2;
p->balance = b;
#else
b2 = (ubalance_t)b >> (8 * sizeof(b2) - 1);
p->balance = b2;
b2 = -b2 -b;
r->balance = b2;
#endif
t->balance = 0;
s = p->parent;
p->parent = t;
r->parent = t;
if (t->parent = s) {
if ( s->left == p) {
s->left = t;
} else {
s->right = t;
}
} else {
free_tree = t;
}
}
break;
} else /* p->balance == +1 */ {
p->balance = 0;
/* growth of left side balanced the node */
break;
}
} else /* r == p->right */ {
balance_t b;
if ( !(b = p->balance) ) {
/* growth propagation from right side */
p->balance++;
} else if (b > 0) {
if (r->balance > 0) {
/* L-Rotation */
if (p->right = s = r->left) {
s->parent = p;
}
r->left = p;
p->balance = 0;
r->balance = 0;
s = p->parent;
p->parent = r;
if (r->parent = s) {
if ( s->left == p) {
s->left = r;
} else {
s->right = r;
}
} else {
free_tree = r;
}
} else /* r->balance == -1 */ {
/* RL-Rotation */
balance_t b2;
struct free_block *t = r->left;
if (p->right = s = t->left ) {
s->parent = p;
}
/* relocated left subtree */
t->left = p;
if (r->left = s = t->right) {
s->parent = r;
}
/* relocated right subtree */
t->right = r;
b = t->balance;
#ifdef NO_BARREL_SHIFT
b = -b;
b2 = b >> 1;
p->balance = b2;
b -= b2;
r->balance = b;
#else
b2 = (ubalance_t)b >> (8 * sizeof(b2) - 1);
r->balance = b2;
b2 = -b2 -b;
p->balance = b2;
#endif
t->balance = 0;
s = p->parent;
p->parent = t;
r->parent = t;
if (t->parent = s) {
if ( s->left == p) {
s->left = t;
} else {
s->right = t;
}
} else {
free_tree = t;
}
}
break;
} else /* p->balance == -1 */ {
p->balance = 0;
/* growth of right side balanced the node */
break;
}
}
r = p;
p = p->parent;
} while (q = p);
}
static union svalue alloc_large_block(p_int type, p_uint size)
{
struct free_block *fit, *q;
retry:
for (fit = 0, q = free_tree; ; ) {
struct free_block *p;
p_uint tempsize;
p = q;
tempsize = p->size;
if (size < tempsize) {
fit = p; /* remember this fit */
if (q = p->left) {
continue;
}
/* We don't need that much, but that's the best fit we have */
break;
} else if (size > tempsize) {
if (q = p->right) {
continue;
}
break;
} else /* size == tempsize */ {
fit = p;
break;
}
} /* end for */
if (fit) {
p_uint excess_size;
remove_from_free_tree(fit);
if ((excess_size = fit->size - size)) {
if (excess_size > SMALL_BLOCK_CMAX) {
add_to_free_tree(
(struct free_block *)((uint8 *)fit+size),
excess_size
);
} else {
((uint8 *)fit)[size-sizeof(char *)] = T_SMALLFREE;
((uint8 *)fit)[size-sizeof(char *)+1] = excess_size;
}
} else {
((uint8 *)fit)[size-sizeof(char *)] &= ~AL_PREV_FREE;
}
((p_int *)fit)[-1] = type | ( ((p_int *)fit)[-1] & AL_PREV_FREE );
return (union svalue)(p_int) ( (uint8 *)fit - sizeof(char *) + 1 );
} else {
static char mess1[] =
"Temporary out of MEMORY. Freeing user reserve.\n";
static char mess2[] =
"Temporary out of MEMORY. Freeing master reserve.\n";
static char mess3[] =
"Temporary out of MEMORY. Freeing system reserve.\n";
static char mess4[] =
"Totally out of MEMORY.\n";
#define FREE_RESERVED_AREA(area) free_large_block(area.p,((p_int *)(area.p-1))[1])
SET_JOB(garbage_collection);
if (reserved_user_area.p) {
FREE_RESERVED_AREA(reserved_user_area);
reserved_user_area.p = 0;
write(2, mess1, sizeof(mess1)-1);
goto retry;
}
if (malloc_privilege >= ALLOC_MASTER && reserved_master_area.p) {
FREE_RESERVED_AREA(reserved_master_area);
reserved_master_area.p = 0;
write(2, mess2, sizeof(mess2)-1);
goto retry;
}
if (malloc_privilege >= ALLOC_SYSTEM) {
if (reserved_system_area.p) {
FREE_RESERVED_AREA(reserved_system_area);
reserved_system_area.p = 0;
write(2, mess3, sizeof(mess3)-1);
goto retry;
}
write(2, mess4, sizeof(mess4)-1);
fatal("Out of memory\n");
}
adtstat[ALLOC_LALLOC]--;
adtstat[ALLOC_LALLOC_TOTAL] -= size;
out_of_memory = 1;
if (!inter_errno)
inter_errno = IE_NOMEM;
return SV_NULL;
}
}
void *x_alloc(mp_int size) {
union svalue block;
block = ALLOC(T_INTERNAL, IT_X_ALLOCED, size += sizeof(char *));
if (block.i) {
*(p_int *)(block.p + sizeof(char *) - 1) = size;
block.p += 2*sizeof(char *) - 1;
}
return (void *)block.p;
}
void x_free(void *p) {
free_block((char *)p - 2 * sizeof(char *) + 1, ((p_int *)p)[-1]);
}
void *re_x_alloc(void *old, mp_int size) {
void *new;
mp_int old_size;
new = x_alloc(size);
old_size = ((p_int *)old)[-1] - sizeof(char *);
if (old_size > size)
old_size = size;
amemcpy(new, old, old_size);
x_free(old);
return new;
}