| 1 | /* $Id$ $Revision$ */ |
| 2 | /* vim:set shiftwidth=4 ts=8: */ |
| 3 | |
| 4 | /************************************************************************* |
| 5 | * Copyright (c) 2011 AT&T Intellectual Property |
| 6 | * All rights reserved. This program and the accompanying materials |
| 7 | * are made available under the terms of the Eclipse Public License v1.0 |
| 8 | * which accompanies this distribution, and is available at |
| 9 | * http://www.eclipse.org/legal/epl-v10.html |
| 10 | * |
| 11 | * Contributors: See CVS logs. Details at http://www.graphviz.org/ |
| 12 | *************************************************************************/ |
| 13 | |
| 14 | #include "vmhdr.h" |
| 15 | |
| 16 | #if 0 /* not used */ |
| 17 | static char *Version = "\n@(#)Vmalloc (AT&T Labs - kpv) 1999-08-05\0\n" ; |
| 18 | #endif |
| 19 | |
| 20 | |
| 21 | /* Private code used in the vmalloc library |
| 22 | ** |
| 23 | ** Written by Kiem-Phong Vo, kpv@research.att.com, 01/16/94. |
| 24 | */ |
| 25 | |
| 26 | /** |
| 27 | * Get more memory for a region |
| 28 | * |
| 29 | * @param vm region to increase in size |
| 30 | * @param size desired amount of space |
| 31 | * @param searchf tree search function |
| 32 | */ |
| 33 | static Block_t *vmextend(reg Vmalloc_t * vm, size_t size, |
| 34 | Vmsearch_f searchf) |
| 35 | { |
| 36 | reg size_t s; |
| 37 | reg Seg_t *seg; |
| 38 | reg Block_t *bp, *t; |
| 39 | reg Vmuchar_t *addr; |
| 40 | reg Vmdata_t *vd = vm->data; |
| 41 | reg Vmemory_f memoryf = vm->disc->memoryf; |
| 42 | reg Vmexcept_f exceptf = vm->disc->exceptf; |
| 43 | |
| 44 | GETPAGESIZE(_Vmpagesize); |
| 45 | |
| 46 | if (vd->incr <= 0) /* this is just _Vmheap on the first call */ |
| 47 | vd->incr = 4 * _Vmpagesize; |
| 48 | |
| 49 | /* Get slightly more for administrative data */ |
| 50 | s = size + sizeof(Seg_t) + sizeof(Block_t) + sizeof(Head_t) + |
| 51 | 2 * ALIGN; |
| 52 | if (s <= size) /* size was too large and we have wrapped around */ |
| 53 | return NIL(Block_t *); |
| 54 | if ((size = ROUND(s, vd->incr)) < s) |
| 55 | return NIL(Block_t *); |
| 56 | |
| 57 | /* see if we can extend the current segment */ |
| 58 | if (!(seg = vd->seg)) |
| 59 | addr = NIL(Vmuchar_t *); |
| 60 | else { |
| 61 | if (!vd->wild || SEG(vd->wild) != seg) |
| 62 | s = 0; |
| 63 | else { |
| 64 | s = SIZE(vd->wild) + sizeof(Head_t); |
| 65 | if ((s = (s / vd->incr) * vd->incr) == size) |
| 66 | size += vd->incr; |
| 67 | } |
| 68 | addr = (Vmuchar_t *) (*memoryf) (vm, seg->addr, seg->extent, |
| 69 | seg->extent + size - s, vm->disc); |
| 70 | if (!addr) |
| 71 | seg = NIL(Seg_t *); |
| 72 | else { |
| 73 | /**/ ASSERT(addr == (Vmuchar_t *) seg->addr); |
| 74 | addr += seg->extent; |
| 75 | size -= s; |
| 76 | } |
| 77 | } |
| 78 | |
| 79 | while (!addr) { /* try to get space */ |
| 80 | if ((addr = |
| 81 | (Vmuchar_t *) (*memoryf) (vm, NIL(void *), 0, size, |
| 82 | vm->disc))) |
| 83 | break; |
| 84 | |
| 85 | /* check with exception handler to see if we should continue */ |
| 86 | if (!exceptf) |
| 87 | return NIL(Block_t *); |
| 88 | else { |
| 89 | int rv, lock; |
| 90 | lock = vd->mode & VM_LOCK; |
| 91 | vd->mode &= ~VM_LOCK; |
| 92 | rv = (*exceptf) (vm, VM_NOMEM, (void *) size, vm->disc); |
| 93 | vd->mode |= lock; |
| 94 | if (rv <= 0) { |
| 95 | if (rv == 0) |
| 96 | vd->mode |= VM_AGAIN; |
| 97 | return NIL(Block_t *); |
| 98 | } |
| 99 | } |
| 100 | } |
| 101 | |
| 102 | if (seg) { /* extending current segment */ |
| 103 | bp = BLOCK(seg->baddr); |
| 104 | /**/ ASSERT((SIZE(bp) & ~BITS) == 0); |
| 105 | /**/ ASSERT(SEG(bp) == seg); |
| 106 | |
| 107 | if (vd->mode & (VM_MTBEST | VM_MTDEBUG | VM_MTPROFILE)) { |
| 108 | if (!ISPFREE(SIZE(bp))) |
| 109 | SIZE(bp) = size - sizeof(Head_t); |
| 110 | else { |
| 111 | /**/ ASSERT(searchf); |
| 112 | bp = LAST(bp); |
| 113 | if (bp == vd->wild) |
| 114 | vd->wild = NIL(Block_t *); |
| 115 | else |
| 116 | REMOVE(vd, bp, INDEX(SIZE(bp)), t, (*searchf)); |
| 117 | SIZE(bp) += size; |
| 118 | } |
| 119 | } else { |
| 120 | if (seg->free) { |
| 121 | bp = seg->free; |
| 122 | seg->free = NIL(Block_t *); |
| 123 | SIZE(bp) += size; |
| 124 | } else |
| 125 | SIZE(bp) = size - sizeof(Head_t); |
| 126 | } |
| 127 | |
| 128 | seg->size += size; |
| 129 | seg->extent += size; |
| 130 | seg->baddr += size; |
| 131 | } else { /* creating a new segment */ |
| 132 | reg Seg_t *sp, *lastsp; |
| 133 | |
| 134 | if ((s = (size_t) (VLONG(addr) % ALIGN)) != 0) |
| 135 | addr += ALIGN - s; |
| 136 | |
| 137 | seg = (Seg_t *) addr; |
| 138 | seg->vm = vm; |
| 139 | seg->addr = (void *) (addr - (s ? ALIGN - s : 0)); |
| 140 | seg->extent = size; |
| 141 | seg->baddr = addr + size - (s ? 2 * ALIGN : 0); |
| 142 | seg->free = NIL(Block_t *); |
| 143 | bp = SEGBLOCK(seg); |
| 144 | SEG(bp) = seg; |
| 145 | SIZE(bp) = seg->baddr - (Vmuchar_t *) bp - 2 * sizeof(Head_t); |
| 146 | |
| 147 | /* NOTE: for Vmbest, Vmdebug and Vmprofile the region's segment list |
| 148 | is reversely ordered by addresses. This is so that we can easily |
| 149 | check for the wild block. |
| 150 | */ |
| 151 | lastsp = NIL(Seg_t *); |
| 152 | sp = vd->seg; |
| 153 | if (vd->mode & (VM_MTBEST | VM_MTDEBUG | VM_MTPROFILE)) |
| 154 | for (; sp; lastsp = sp, sp = sp->next) |
| 155 | if (seg->addr > sp->addr) |
| 156 | break; |
| 157 | seg->next = sp; |
| 158 | if (lastsp) |
| 159 | lastsp->next = seg; |
| 160 | else |
| 161 | vd->seg = seg; |
| 162 | |
| 163 | seg->size = SIZE(bp); |
| 164 | } |
| 165 | |
| 166 | /* make a fake header for possible segmented memory */ |
| 167 | t = NEXT(bp); |
| 168 | SEG(t) = seg; |
| 169 | SIZE(t) = BUSY; |
| 170 | |
| 171 | /* see if the wild block is still wild */ |
| 172 | if ((t = vd->wild) && (seg = SEG(t)) != vd->seg) { |
| 173 | CLRPFREE(SIZE(NEXT(t))); |
| 174 | if (vd->mode & (VM_MTBEST | VM_MTDEBUG | VM_MTPROFILE)) { |
| 175 | SIZE(t) |= BUSY | JUNK; |
| 176 | LINK(t) = CACHE(vd)[C_INDEX(SIZE(t))]; |
| 177 | CACHE(vd)[C_INDEX(SIZE(t))] = t; |
| 178 | } else |
| 179 | seg->free = t; |
| 180 | |
| 181 | vd->wild = NIL(Block_t *); |
| 182 | } |
| 183 | |
| 184 | return bp; |
| 185 | } |
| 186 | |
| 187 | /** |
| 188 | * Truncate a segment if possible |
| 189 | * |
| 190 | * @param vm containing region |
| 191 | * @param seg the one to be truncated |
| 192 | * @param size amount of free space |
| 193 | * @param exact amount given was exact |
| 194 | */ |
| 195 | static int vmtruncate(Vmalloc_t * vm, Seg_t * seg, size_t size, int exact) |
| 196 | { |
| 197 | reg void *caddr; |
| 198 | reg Seg_t *last; |
| 199 | reg Vmdata_t *vd = vm->data; |
| 200 | reg Vmemory_f memoryf = vm->disc->memoryf; |
| 201 | |
| 202 | caddr = seg->addr; |
| 203 | |
| 204 | if (size < seg->size) { |
| 205 | reg size_t less; |
| 206 | |
| 207 | /* the truncated amount must satisfy the discipline requirement */ |
| 208 | if ((less = vm->disc->round) <= 0) |
| 209 | less = _Vmpagesize; |
| 210 | less = (size / less) * less; |
| 211 | less = (less / ALIGN) * ALIGN; |
| 212 | |
| 213 | if (!exact) /* only truncate multiples of incr */ |
| 214 | less = (less / vd->incr) * vd->incr; |
| 215 | |
| 216 | if (less > 0 && size > less && (size - less) < sizeof(Block_t)) |
| 217 | less -= vd->incr; |
| 218 | |
| 219 | if (less <= 0 || |
| 220 | (*memoryf) (vm, caddr, seg->extent, seg->extent - less, |
| 221 | vm->disc) != caddr) |
| 222 | return -1; |
| 223 | |
| 224 | seg->extent -= less; |
| 225 | seg->size -= less; |
| 226 | seg->baddr -= less; |
| 227 | SIZE(BLOCK(seg->baddr)) = BUSY; |
| 228 | return 0; |
| 229 | } |
| 230 | |
| 231 | /* unlink segment from region */ |
| 232 | if (seg == vd->seg) { |
| 233 | vd->seg = seg->next; |
| 234 | last = NIL(Seg_t *); |
| 235 | } else { |
| 236 | for (last = vd->seg; last->next != seg; last = last->next); |
| 237 | last->next = seg->next; |
| 238 | } |
| 239 | |
| 240 | /* now delete it */ |
| 241 | if ((*memoryf) (vm, caddr, seg->extent, 0, vm->disc) == caddr) |
| 242 | return 0; |
| 243 | |
| 244 | /* space reduction failed, reinsert segment */ |
| 245 | if (last) { |
| 246 | seg->next = last->next; |
| 247 | last->next = seg; |
| 248 | } else { |
| 249 | seg->next = vd->seg; |
| 250 | vd->seg = seg; |
| 251 | } |
| 252 | return -1; |
| 253 | } |
| 254 | |
| 255 | /* Externally visible names but local to library */ |
| 256 | Vmextern_t _Vmextern = { vmextend, /* _Vmextend */ |
| 257 | vmtruncate, /* _Vmtruncate */ |
| 258 | 0, /* _Vmpagesize */ |
| 259 | NIL(char *(*)(char *, char *, int)), /* _Vmstrcpy */ |
| 260 | NIL(char *(*)(Vmulong_t, int)), /* _Vmitoa */ |
| 261 | NIL(void (*)(Vmalloc_t *, |
| 262 | Vmuchar_t *, Vmuchar_t *, size_t, size_t)), /* _Vmtrace */ |
| 263 | NIL(void (*)(Vmalloc_t *)) /* _Vmpfclose */ |
| 264 | }; |
| 265 | |