[9] | 1 | /* SB - Copyright 1982 by Ken Harrenstien, SRI International
|
---|
| 2 | * This software is quasi-public; it may be used freely with
|
---|
| 3 | * like software, but may NOT be sold or made part of licensed
|
---|
| 4 | * products without permission of the author. In all cases
|
---|
| 5 | * the source code and any modifications thereto must remain
|
---|
| 6 | * available to any user.
|
---|
| 7 | *
|
---|
| 8 | * This is part of the SB library package.
|
---|
| 9 | * Any software using the SB library must likewise be made
|
---|
| 10 | * quasi-public, with freely available sources.
|
---|
| 11 | */
|
---|
| 12 |
|
---|
| 13 | #if 0
|
---|
| 14 | This file contains the low-level memory allocation
|
---|
| 15 | subroutines which are used by the SBLK routines. The code here
|
---|
| 16 | is quite machine-dependent, and the definitions in "sb.h" should be
|
---|
| 17 | carefully checked to verify that they are correct for the target
|
---|
| 18 | machine.
|
---|
| 19 |
|
---|
| 20 | The ultimate low-level routine is "sbrk()" which must be
|
---|
| 21 | provided by the system''s C library. SBM expects that successive calls
|
---|
| 22 | to sbrk() will return contiguous areas of memory with progressively
|
---|
| 23 | higher addresses. Also, the very first call to sbrk() is assumed to
|
---|
| 24 | return a word-aligned address.
|
---|
| 25 | #endif /*COMMENT*/
|
---|
| 26 |
|
---|
| 27 | #include "sb.h"
|
---|
| 28 |
|
---|
| 29 | #define FUDGE (sizeof(struct smblk)) /* Allow this much fudge in
|
---|
| 30 | allocation, to prevent undue fragmentation */
|
---|
| 31 |
|
---|
| 32 | char *(*sbm_debug)(); /* Debug switch - user-furnished routine */
|
---|
| 33 |
|
---|
| 34 | struct smblk *sbm_nfl; /* Pointer to node freelist */
|
---|
| 35 | struct smblk *sbm_nxtra; /* Reserved extra free node */
|
---|
| 36 | struct smblk *sbm_list; /* Pointer to smblk memory alloc list.
|
---|
| 37 | * ALL smblks are strung onto this list
|
---|
| 38 | * except for the freelist!
|
---|
| 39 | */
|
---|
| 40 | SBMA sbm_lowaddr; /* Lowest word-aligned address we know about.*/
|
---|
| 41 |
|
---|
| 42 | /* If compiling with debug switch set, use special routine in place of
|
---|
| 43 | * sbrk so we can pretend we have a very limited area of free memory.
|
---|
| 44 | */
|
---|
| 45 | #ifdef DBG_SIZE
|
---|
| 46 | #define SBM_SBRK sbm_brk
|
---|
| 47 | char *sbm_brk();
|
---|
| 48 | #else
|
---|
| 49 | #define SBM_SBRK sbrk
|
---|
| 50 | #endif /*DBG_SIZE*/
|
---|
| 51 |
|
---|
| 52 | /* Forward routine declarations */
|
---|
| 53 | char *sbrk();
|
---|
| 54 | struct smblk *sbm_nmak(), *sbm_nget(), *sbm_mget(), *sbm_split();
|
---|
| 55 | struct smblk *sbm_lmak(), *sbm_err();
|
---|
| 56 | |
---|
| 57 |
|
---|
| 58 | /* SBM_INIT - Initialize storage management.
|
---|
| 59 | * If args are zero, normal initialization is done. Otherwise,
|
---|
| 60 | * args are understood to be pointers to an area of memory allocated
|
---|
| 61 | * on the stack (eg by an "int mem[2000]" declaration in MAIN) and
|
---|
| 62 | * initialization will include this area in addition to the
|
---|
| 63 | * unused space between "_end" and the start of the stack segment.
|
---|
| 64 | * This is mostly of use for PDP11s which would otherwise waste a lot
|
---|
| 65 | * of address space.
|
---|
| 66 | * Maybe should have a SBM_RESET() function?
|
---|
| 67 | */
|
---|
| 68 |
|
---|
| 69 | struct smblk *
|
---|
| 70 | sbm_init(xaddr,xlen)
|
---|
| 71 | SBMA xaddr; /* Address of allocated stack area if any */
|
---|
| 72 | SBMO xlen; /* Size of this area */
|
---|
| 73 | { register struct smblk *sm, *sml;
|
---|
| 74 | register char *cp;
|
---|
| 75 |
|
---|
| 76 | /* Get initial chunk of memory from standard system rtn */
|
---|
| 77 | if((cp = SBM_SBRK(SMNODES*sizeof(struct smblk))) == 0
|
---|
| 78 | || (int) cp == -1)
|
---|
| 79 | return(sbm_err(0,"Can't sbrk"));
|
---|
| 80 | sm = (struct smblk *)cp; /* Better be word-aligned! */
|
---|
| 81 | sbm_lmak(sm,(SBMO)sizeof(struct smblk),SMNODES); /* Make list */
|
---|
| 82 | sbm_nfl = sm; /* Point freelist at it */
|
---|
| 83 | sbm_lowaddr = (SBMA)sm; /* Remember lowest addr seen */
|
---|
| 84 |
|
---|
| 85 | /* Set up 1st node pointing to all memory from here on up.
|
---|
| 86 | * We don't know exactly how much will be available at this point,
|
---|
| 87 | * so we just pretend we have the maximum possible.
|
---|
| 88 | */
|
---|
| 89 | sbm_list = sml = sbm_nget();
|
---|
| 90 | sml->smforw = sml->smback = 0;
|
---|
| 91 | sml->smflags = SM_USE|SM_NID; /* Initial flags */
|
---|
| 92 | sml->smaddr = (SBMA) sml;
|
---|
| 93 | sml->smlen = MAXSBMO; /* Pretend we have lots */
|
---|
| 94 | sml->smuse = (SMNODES * sizeof(struct smblk));
|
---|
| 95 |
|
---|
| 96 | /* Now split off everything above initial allocation as NXM. */
|
---|
| 97 | sm = sbm_split(sml, sm->smuse);
|
---|
| 98 | sml->smflags |= SM_MNODS; /* Mark 1st node as having SM nodes */
|
---|
| 99 | sm->smflags |= SM_NXM; /* Mark 2nd node as NXM */
|
---|
| 100 |
|
---|
| 101 | /* Now possibly set up extra nodes, if stack mem is being allocated
|
---|
| 102 | * (From our viewpoint it looks as if a chunk in the middle of
|
---|
| 103 | * the initial NXM section has been declared usable)
|
---|
| 104 | */
|
---|
| 105 | if(xlen)
|
---|
| 106 | { /* Allow for "extra" static stack memory */
|
---|
| 107 | /* Will lose if xaddr <= 1st NXM! */
|
---|
| 108 | sml = sbm_split(sm, (SBMO)(xaddr - sm->smaddr));
|
---|
| 109 | sbm_split(sml, xlen); /* Split off following NXM */
|
---|
| 110 | sml->smflags &= ~(SM_USE|SM_NXM); /* This node is free mem! */
|
---|
| 111 | }
|
---|
| 112 |
|
---|
| 113 | /* Now set up a small additional node which points to the NXM
|
---|
| 114 | * that we cannot get from SBRK. At this stage, this is just
|
---|
| 115 | * a place-holder, to reserve the node so we don't have to
|
---|
| 116 | * worry about running out of nodes at the same time sbrk stops
|
---|
| 117 | * returning memory.
|
---|
| 118 | * SM points to the NXM that we expect SBRK to dig into.
|
---|
| 119 | */
|
---|
| 120 | sbm_split(sm, sm->smlen - WDSIZE); /* Chop off teensy bit */
|
---|
| 121 | sm->smflags &= ~SM_USE; /* Now mark NXM "free"!! */
|
---|
| 122 |
|
---|
| 123 | /* Finally, reserve an "extra" SM node for use by sbm_nget
|
---|
| 124 | * when it is allocating more freelist node chunks.
|
---|
| 125 | */
|
---|
| 126 | sbm_nxtra = sbm_nget();
|
---|
| 127 |
|
---|
| 128 | return(sbm_list);
|
---|
| 129 | }
|
---|
| 130 | |
---|
| 131 |
|
---|
| 132 | /* SBM_NGET() - Get a free SM node.
|
---|
| 133 | * Note the hair to provide a spare SM node when
|
---|
| 134 | * we are allocating memory for more SM nodes. This is necessary
|
---|
| 135 | * because sbm_mget and sbm_nget call each other recursively and
|
---|
| 136 | * sbm_mget cannot create any new memory without a SM node to point
|
---|
| 137 | * at the allocated chunk.
|
---|
| 138 | */
|
---|
| 139 | struct smblk *
|
---|
| 140 | sbm_nget()
|
---|
| 141 | { register struct smblk *sm, *sml;
|
---|
| 142 |
|
---|
| 143 | if(!(sm = sbm_nfl)) /* Get a node from freelist */
|
---|
| 144 | { /* Freelist is empty, try to allocate more nodes. */
|
---|
| 145 |
|
---|
| 146 | /* Put our "spare" smblk on freelist temporarily so that
|
---|
| 147 | * sbm_mget has a chance of winning.
|
---|
| 148 | * Infinite recursion is avoided by a test
|
---|
| 149 | * in sbm_mget which checks sbm_nfl and sbm_nxtra.
|
---|
| 150 | */
|
---|
| 151 | if(!(sm = sbm_nxtra))
|
---|
| 152 | return(sbm_err(0,"Zero sbm_nxtra!"));
|
---|
| 153 | sm->smforw = 0;
|
---|
| 154 | sbm_nfl = sm;
|
---|
| 155 | sbm_nxtra = 0;
|
---|
| 156 |
|
---|
| 157 | /* Try to allocate another chunk of SM nodes. */
|
---|
| 158 | sml = sbm_nmak(sizeof(struct smblk),SM_MNODS);
|
---|
| 159 |
|
---|
| 160 | /* Put the new free nodes (if any) on freelist.
|
---|
| 161 | * Done this way because freelist may have had one or two
|
---|
| 162 | * nodes added to it by sbm_mget, so can't just stick
|
---|
| 163 | * a new pointer in sbm_nfl.
|
---|
| 164 | */
|
---|
| 165 | while(sm = sml)
|
---|
| 166 | { sml = sm->smforw;
|
---|
| 167 | sbm_nfre(sm);
|
---|
| 168 | }
|
---|
| 169 |
|
---|
| 170 | /* Now reserve an extra node again.
|
---|
| 171 | * It is an error if there is nothing on freelist here,
|
---|
| 172 | * because even if sbm_mget failed the "extra node" should
|
---|
| 173 | * still be on freelist. The check for a zero sbm_nxtra
|
---|
| 174 | * above will catch such an error.
|
---|
| 175 | */
|
---|
| 176 | sbm_nxtra = sbm_nget();
|
---|
| 177 |
|
---|
| 178 | /* Now see if anything to return */
|
---|
| 179 | if(!(sm = sbm_nfl)) /* If freelist empty again, */
|
---|
| 180 | return(0); /* give up. */
|
---|
| 181 | }
|
---|
| 182 | sbm_nfl = sm->smforw; /* If win, take it off freelist */
|
---|
| 183 | return(sm); /* Return ptr or 0 if none */
|
---|
| 184 | }
|
---|
| 185 |
|
---|
| 186 | /* SBM_NFRE(sm) - Return a SM node to the SM freelist.
|
---|
| 187 | */
|
---|
| 188 | sbm_nfre(smp)
|
---|
| 189 | struct smblk *smp;
|
---|
| 190 | { register struct smblk *sm;
|
---|
| 191 | (sm = smp)->smflags = 0;
|
---|
| 192 | sm->smforw = sbm_nfl;
|
---|
| 193 | sbm_nfl = sm;
|
---|
| 194 | }
|
---|
| 195 |
|
---|
| 196 | /* SBM_NMAK(elsize, flag) - Make (allocate & build) a typeless node freelist.
|
---|
| 197 | */
|
---|
| 198 | struct smblk *
|
---|
| 199 | sbm_nmak(elsize, flag)
|
---|
| 200 | SBMO elsize;
|
---|
| 201 | unsigned flag;
|
---|
| 202 | { register struct smblk *sm, *smp;
|
---|
| 203 | register int cnt;
|
---|
| 204 |
|
---|
| 205 | if((sm = sbm_mget(SMNODES*elsize,SMNODES*elsize)) == 0)
|
---|
| 206 | return(0);
|
---|
| 207 |
|
---|
| 208 | sm->smflags |= flag; /* Indicate type of nodes */
|
---|
| 209 | cnt = sm->smlen/elsize; /* Find # nodes that will fit */
|
---|
| 210 | sm->smuse = cnt * elsize; /* Actual size used */
|
---|
| 211 | smp = (struct smblk *)(sm->smaddr); /* Ptr to 1st loc of mem */
|
---|
| 212 | sbm_lmak(smp, (SBMO)elsize, cnt); /* Build freelist */
|
---|
| 213 | return(smp); /* Return 1st free node. Caller is */
|
---|
| 214 | /* responsible for setting freelist ptr. */
|
---|
| 215 | }
|
---|
| 216 |
|
---|
| 217 | /* SBM_LMAK - Build freelist of typeless nodes.
|
---|
| 218 | * Note this does not allocate memory, it just converts an already
|
---|
| 219 | * allocated memory area.
|
---|
| 220 | */
|
---|
| 221 | struct smblk *
|
---|
| 222 | sbm_lmak(addr, elsize, num)
|
---|
| 223 | SBMA addr;
|
---|
| 224 | SBMO elsize;
|
---|
| 225 | int num;
|
---|
| 226 | { register struct smblk *sm, *smp;
|
---|
| 227 | register int cnt;
|
---|
| 228 |
|
---|
| 229 | smp = (struct smblk *) addr;
|
---|
| 230 | if((cnt = num) <= 0)
|
---|
| 231 | return(0);
|
---|
| 232 | do { sm = smp; /* Save ptr */
|
---|
| 233 | sm->smforw = (smp = (struct smblk *) ((SBMA)smp + elsize));
|
---|
| 234 | sm->smflags = 0;
|
---|
| 235 | } while(--cnt);
|
---|
| 236 | sm->smforw = 0; /* Last node points to nothing */
|
---|
| 237 | return(sm); /* Return ptr to last node */
|
---|
| 238 | }
|
---|
| 239 |
|
---|
| 240 | /* SBM_NMOV(sm1, sm2, begp, elsize) - Move a typeless node.
|
---|
| 241 | * Copy sm1 to sm2, adjust ptrs, leave sm1 free.
|
---|
| 242 | */
|
---|
| 243 | sbm_nmov(smp1,smp2,begp,elsize)
|
---|
| 244 | struct smblk *smp1, *smp2, **begp;
|
---|
| 245 | int elsize;
|
---|
| 246 | { register struct smblk *sm;
|
---|
| 247 |
|
---|
| 248 | bcopy((SBMA)smp1,(SBMA)(sm = smp2), elsize); /* Copy the stuff */
|
---|
| 249 | if(sm->smforw) sm->smforw->smback = sm; /* Fix up links */
|
---|
| 250 | if(sm->smback) sm->smback->smforw = sm;
|
---|
| 251 | else *begp = sm;
|
---|
| 252 | }
|
---|
| 253 | |
---|
| 254 |
|
---|
| 255 | /* SBM_MGET(min,max) - Get a SMBLK with specified amount of memory.
|
---|
| 256 | * Returns 0 if none available.
|
---|
| 257 | * Memory is guaranteed to start on word boundary, but may not
|
---|
| 258 | * end on one. Note that sbm_mfree is responsible for
|
---|
| 259 | * ensuring that free mem starts word-aligned.
|
---|
| 260 | * A subtle but major concern of this code is the number of freelist
|
---|
| 261 | * nodes gobbled by a single call. If the freelist happens to not have
|
---|
| 262 | * enough nodes, then a recursive call to sbm_mget is made (via sbm_nget)
|
---|
| 263 | * in order to allocate a new batch of freelist nodes! sbm_nget will
|
---|
| 264 | * always provide a single "spare" node during such an allocation, but
|
---|
| 265 | * there is only one and it is essential that sbm_mget gobble only ONE
|
---|
| 266 | * (if any) during such a call, which is indicated by sbm_nxtra==0.
|
---|
| 267 | * The maximum # of freelist nodes that sbm_mget can gobble is
|
---|
| 268 | * 2, when (1) NXM memory is obtained, and a SM is needed to point at
|
---|
| 269 | * the new free mem, plus (2) the resulting SM is too big, and has to
|
---|
| 270 | * be split up, which requires another SM for the remainder.
|
---|
| 271 | * The "used-NXM" smblk is set up at init time precisely in order to
|
---|
| 272 | * avoid the necessity of creating it here when sbrk stops winning, since
|
---|
| 273 | * that would require yet another freelist node and make it possible for
|
---|
| 274 | * sbm_mget to gobble 3 during one call -- too many.
|
---|
| 275 | * Further note: the sbm_nfl checks are necessary in order
|
---|
| 276 | * to ensure that a SM node is available for use by sbm_split. Otherwise
|
---|
| 277 | * the calls to sbm_split might create a new SM freelist by gobbling the
|
---|
| 278 | * very memory which we are hoping to return!
|
---|
| 279 | */
|
---|
| 280 | SBMO sbm_chksiz = SMCHUNKSIZ; /* Current chunk size to feed sbrk */
|
---|
| 281 |
|
---|
| 282 | struct smblk *
|
---|
| 283 | sbm_mget(cmin,cmax)
|
---|
| 284 | SBMO cmin,cmax;
|
---|
| 285 | { register struct smblk *sm, *sml;
|
---|
| 286 | register SBMO csiz;
|
---|
| 287 | register SBMA addr, xaddr;
|
---|
| 288 |
|
---|
| 289 | if((sm = sbm_list) == 0 /* If never done, */
|
---|
| 290 | && (sm = sbm_init((SBMA)0,(SBMO)0)) == 0) /* initialize mem alloc stuff. */
|
---|
| 291 | return(0); /* Can't init??? */
|
---|
| 292 |
|
---|
| 293 | /* Round up sizes to word boundary */
|
---|
| 294 | if(rndrem(cmin)) cmin = rndup(cmin);
|
---|
| 295 | if(rndrem(cmax)) cmax = rndup(cmax);
|
---|
| 296 |
|
---|
| 297 | /* Search for a free block having enough memory.
|
---|
| 298 | * If run into a free-NXM block, always "win", since there may be
|
---|
| 299 | * a combination of preceding free-mem and new mem which will satisfy
|
---|
| 300 | * the request. If it turns out this didn't work, we'll just fail
|
---|
| 301 | * a little farther on.
|
---|
| 302 | */
|
---|
| 303 | retry: csiz = cmin; /* Set size that will satisfy us */
|
---|
| 304 | do {
|
---|
| 305 | if( ((sm->smflags&SM_USE) == 0)
|
---|
| 306 | && ((sm->smlen >= csiz) || (sm->smflags&SM_NXM)) )
|
---|
| 307 | break;
|
---|
| 308 | } while(sm = sm->smforw);
|
---|
| 309 | if(sm == 0)
|
---|
| 310 | return(0); /* Found none that minimum would fit */
|
---|
| 311 |
|
---|
| 312 | if(sm->smflags&SM_NXM)
|
---|
| 313 | { /* Found free area, but it's marked NXM and the system
|
---|
| 314 | * must be persuaded (via sbrk) to let us use that portion
|
---|
| 315 | * of our address space. Grab a good-sized chunk.
|
---|
| 316 | */
|
---|
| 317 | if(sbm_nfl == 0) /* Verify a spare SM node is avail */
|
---|
| 318 | goto getnod; /* Nope, must get one. */
|
---|
| 319 |
|
---|
| 320 | /* Decide amount of mem to ask system for, via sbrk.
|
---|
| 321 | * The fine point here is the check of sbm_nxtra to make sure
|
---|
| 322 | * that, when building more freelist nodes, we don't have
|
---|
| 323 | * to use more than one SM node in the process. If we
|
---|
| 324 | * asked for too much mem, we'd have to use a SM node
|
---|
| 325 | * to hold the excess after splitting.
|
---|
| 326 | */
|
---|
| 327 | csiz = cmax;
|
---|
| 328 | if(sbm_nxtra /* If normal then try for big chunk */
|
---|
| 329 | && csiz < sbm_chksiz) csiz = sbm_chksiz; /* Max */
|
---|
| 330 | if (csiz > sm->smlen) csiz = sm->smlen; /* Min */
|
---|
| 331 |
|
---|
| 332 | /* Get the NXM mem */
|
---|
| 333 | if((addr = (SBMA)SBM_SBRK(csiz)) != sm->smaddr)
|
---|
| 334 | { /* Unexpected value returned from SBRK! */
|
---|
| 335 |
|
---|
| 336 | if((int)addr != 0 && (int)addr != -1)
|
---|
| 337 | { return(sbm_err(0,"SBRK %o != %o", addr,
|
---|
| 338 | sm->smaddr));
|
---|
| 339 | #if 0
|
---|
| 340 | /* If value indicates couldn't get the stuff, then
|
---|
| 341 | * we have probably hit our limit and the rest of
|
---|
| 342 | * NXM should be declared "used" to prevent further
|
---|
| 343 | * hopeless sbrk calls. We split off the portion
|
---|
| 344 | * of NXM that is known for sure to be unavailable,
|
---|
| 345 | * and mark it "used". If a "used NXM" area already
|
---|
| 346 | * exists following this one, the two are merged.
|
---|
| 347 | * The chunk size is then reduced by half, so
|
---|
| 348 | * only log2(SMCHUNKSIZ) attempts will be made, and
|
---|
| 349 | * we try again.
|
---|
| 350 | */
|
---|
| 351 | /* If returned some mem which starts outside
|
---|
| 352 | * the NXM then something is screwed up. */
|
---|
| 353 | if(addr < sm->smaddr
|
---|
| 354 | || (addr >= sm->smaddr+sm->smlen))
|
---|
| 355 | return(sbm_err(0,"SBRK %o != %o",
|
---|
| 356 | addr, sm->smaddr));
|
---|
| 357 | /* Got some mem, falls within NXM.
|
---|
| 358 | * Presumably someone else has called sbrk
|
---|
| 359 | * since last time, so we need to fence off
|
---|
| 360 | * the intervening area. */
|
---|
| 361 | sm = sbm_split((sml=sm),(addr - sm->smaddr));
|
---|
| 362 | sml->smflags |= SM_USE|SM_EXT;
|
---|
| 363 | return(sbm_mget(cmin,cmax));
|
---|
| 364 | #endif /*COMMENT*/
|
---|
| 365 | }
|
---|
| 366 |
|
---|
| 367 | /* Handle case of SBRK claiming no more memory.
|
---|
| 368 | * Gobble as much as we can, and then turn this NXM
|
---|
| 369 | * block into a free-mem block, and leave the
|
---|
| 370 | * remainder in the used-NXM block (which should
|
---|
| 371 | * immediately follow this free-NXM block!)
|
---|
| 372 | */
|
---|
| 373 | if(!(sml = sm->smforw) /* Ensure have used-NXM blk */
|
---|
| 374 | || (sml->smflags&(SM_USE|SM_NXM))
|
---|
| 375 | != (SM_USE|SM_NXM))
|
---|
| 376 | return(sbm_err(0,"No uNXM node!"));
|
---|
| 377 | xaddr = sm->smaddr; /* Use this for checking */
|
---|
| 378 | sm->smuse = 0; /* Use this for sum */
|
---|
| 379 | for(csiz = sm->smlen; csiz > 0;)
|
---|
| 380 | { addr = SBM_SBRK(csiz);
|
---|
| 381 | if((int)addr == 0 || (int)addr == -1)
|
---|
| 382 | { csiz >>= 1;
|
---|
| 383 | continue;
|
---|
| 384 | }
|
---|
| 385 | if(addr != xaddr)
|
---|
| 386 | return(sbm_err(0,"SBRK %o != %o", addr,
|
---|
| 387 | xaddr));
|
---|
| 388 | sm->smuse += csiz;
|
---|
| 389 | xaddr += csiz;
|
---|
| 390 | }
|
---|
| 391 |
|
---|
| 392 | /* Have gobbled as much from SBRK as we could.
|
---|
| 393 | * Turn the free-NXM block into a free-mem block,
|
---|
| 394 | * unless we got nothing, in which case just merge
|
---|
| 395 | * it into the used-NXM block and continue
|
---|
| 396 | * searching from this point.
|
---|
| 397 | */
|
---|
| 398 | if(!(csiz = sm->smuse)) /* Get total added */
|
---|
| 399 | { sm->smflags = sml->smflags; /* Ugh. */
|
---|
| 400 | sbm_mmrg(sm);
|
---|
| 401 | goto retry; /* Keep looking */
|
---|
| 402 | }
|
---|
| 403 | else
|
---|
| 404 | { sml->smaddr = sm->smaddr + csiz;
|
---|
| 405 | sml->smlen += sm->smlen - csiz;
|
---|
| 406 | sm->smlen = csiz;
|
---|
| 407 | sm->smflags &= ~SM_NXM; /* No longer NXM */
|
---|
| 408 | }
|
---|
| 409 | }
|
---|
| 410 |
|
---|
| 411 | /* Here when we've acquired CSIZ more memory from sbrk.
|
---|
| 412 | * If preceding mem area is not in use, merge new mem
|
---|
| 413 | * into it.
|
---|
| 414 | */
|
---|
| 415 | if((sml = sm->smback) &&
|
---|
| 416 | (sml->smflags&(SM_USE|SM_NXM))==0) /* Previous free? */
|
---|
| 417 | { sml->smlen += csiz; /* Yes, simple! */
|
---|
| 418 | sm->smaddr += csiz; /* Fix up */
|
---|
| 419 | if((sm->smlen -= csiz) == 0) /* If no NXM left,*/
|
---|
| 420 | sbm_mmrg(sml); /* Merge NXM node w/prev */
|
---|
| 421 | sm = sml; /* Prev is now winning node */
|
---|
| 422 | }
|
---|
| 423 | else
|
---|
| 424 | { /* Prev node isn't a free area. Split up the NXM
|
---|
| 425 | * node to account for acquired mem, unless we
|
---|
| 426 | * gobbled all the mem available.
|
---|
| 427 | */
|
---|
| 428 | if(sm->smlen > csiz /* Split unless all used */
|
---|
| 429 | && !sbm_split(sm,csiz)) /* Call shd always win */
|
---|
| 430 | return(sbm_err(0,"getsplit err: %o",sm));
|
---|
| 431 | sm->smflags &= ~SM_NXM; /* Node is now real mem */
|
---|
| 432 | }
|
---|
| 433 |
|
---|
| 434 | /* Now make a final check that we have enough memory.
|
---|
| 435 | * This can fail because SBRK may not have been able
|
---|
| 436 | * to gobble enough memory, either because (1) not
|
---|
| 437 | * as much NXM was available as we thought,
|
---|
| 438 | * or (2) we noticed the free-NXM area and immediately
|
---|
| 439 | * gambled on trying it without checking any lengths.
|
---|
| 440 | * In any case, we try again starting from the current SM
|
---|
| 441 | * because there may be more free mem higher up (eg on
|
---|
| 442 | * stack).
|
---|
| 443 | */
|
---|
| 444 | if(sm->smlen < cmin)
|
---|
| 445 | goto retry;
|
---|
| 446 | }
|
---|
| 447 |
|
---|
| 448 | /* Check to see if node has too much mem. This is especially true
|
---|
| 449 | * for memory just acquired via sbrk, which gobbles a huge chunk each
|
---|
| 450 | * time. If there's too much, we split up the area.
|
---|
| 451 | */
|
---|
| 452 | if(sm->smlen > cmax+FUDGE) /* Got too much? (Allow some fudge)*/
|
---|
| 453 | /* Yes, split up so don't gobble too much. */
|
---|
| 454 | if(sbm_nfl) /* If success guaranteed, */
|
---|
| 455 | sbm_split(sm,cmax); /* split it, all's well. */
|
---|
| 456 | else goto getnod;
|
---|
| 457 |
|
---|
| 458 | sm->smuse = 0;
|
---|
| 459 | sm->smflags |= SM_USE; /* Finally seize it by marking "in-use". */
|
---|
| 460 | return(sm);
|
---|
| 461 |
|
---|
| 462 | /* Come here when we will need to get another SM node but the
|
---|
| 463 | * SM freelist is empty. We have to forget about using the area
|
---|
| 464 | * we just found, because sbm_nget may gobble it for the
|
---|
| 465 | * freelist. So, we first force a refill of the freelist, and then
|
---|
| 466 | * invoke ourselves again on what's left.
|
---|
| 467 | */
|
---|
| 468 | getnod:
|
---|
| 469 | if(sml = sbm_nget()) /* Try to build freelist */
|
---|
| 470 | { sbm_nfre(sml); /* Won, give node back, */
|
---|
| 471 | sm = sbm_list; /* and retry, starting over! */
|
---|
| 472 | goto retry;
|
---|
| 473 | }
|
---|
| 474 | /* Failed. Not enough memory for both this request
|
---|
| 475 | * and one more block of SM nodes. Since such a SM_MNODS
|
---|
| 476 | * block isn't very big, we are so close to the limits that it
|
---|
| 477 | * isn't worth trying to do something fancy here to satisfy the
|
---|
| 478 | * original request. So we just fail.
|
---|
| 479 | */
|
---|
| 480 | return(0);
|
---|
| 481 | }
|
---|
| 482 |
|
---|
| 483 | #ifdef DBG_SIZE
|
---|
| 484 | /* Code for debugging stuff by imposing an artificial limitation on size
|
---|
| 485 | * of available memory.
|
---|
| 486 | */
|
---|
| 487 | SBMO sbm_dlim = MAXSBMO; /* Amount of mem to allow (default is max) */
|
---|
| 488 |
|
---|
| 489 | char *
|
---|
| 490 | sbm_brk(size)
|
---|
| 491 | unsigned size;
|
---|
| 492 | { register char *addr;
|
---|
| 493 |
|
---|
| 494 | if(size > sbm_dlim) return(0);
|
---|
| 495 | addr = sbrk(size);
|
---|
| 496 | if((int)addr == 0 || (int)addr == -1)
|
---|
| 497 | return(0);
|
---|
| 498 | sbm_dlim -= size;
|
---|
| 499 | return(addr);
|
---|
| 500 | }
|
---|
| 501 | #endif /*DBG_SIZE*/
|
---|
| 502 | |
---|
| 503 |
|
---|
| 504 | /* SBM_MFREE(sm) - Free up an allocated memory area.
|
---|
| 505 | */
|
---|
| 506 | sbm_mfree(sm)
|
---|
| 507 | register struct smblk *sm;
|
---|
| 508 | { register struct smblk *smx;
|
---|
| 509 | register SBMO crem;
|
---|
| 510 |
|
---|
| 511 | sm->smflags &= ~SM_USE; /* Say mem is free */
|
---|
| 512 | if((smx = sm->smback) /* Check preceding mem */
|
---|
| 513 | && (smx->smflags&(SM_USE|SM_NXM))==0) /* If it's free, */
|
---|
| 514 | sbm_mmrg(sm = smx); /* then merge 'em. */
|
---|
| 515 | if((smx = sm->smforw) /* Check following mem */
|
---|
| 516 | && (smx->smflags&(SM_USE|SM_NXM))==0) /* Again, if free, */
|
---|
| 517 | sbm_mmrg(sm); /* merge them. */
|
---|
| 518 |
|
---|
| 519 | if(sm->smlen == 0) /* Just in case, chk for null blk */
|
---|
| 520 | { if(smx = sm->smback) /* If pred exists, */
|
---|
| 521 | sbm_mmrg(smx); /* merge quietly. */
|
---|
| 522 | else {
|
---|
| 523 | sbm_list = sm->smforw; /* 1st node on list, so */
|
---|
| 524 | sbm_nfre(sm); /* simply flush it. */
|
---|
| 525 | }
|
---|
| 526 | return;
|
---|
| 527 | }
|
---|
| 528 |
|
---|
| 529 | /* This code is slightly over-general for some machines.
|
---|
| 530 | * The pointer subtraction is done in order to get a valid integer
|
---|
| 531 | * offset value regardless of the internal representation of a pointer.
|
---|
| 532 | * We cannot reliably force alignment via casts; some C implementations
|
---|
| 533 | * treat that as a no-op.
|
---|
| 534 | */
|
---|
| 535 | if(crem = rndrem(sm->smaddr - sbm_lowaddr)) /* On word bndry? */
|
---|
| 536 | { /* No -- must adjust. All free mem blks MUST, by fiat,
|
---|
| 537 | * start on word boundary. Here we fix things by
|
---|
| 538 | * making the leftover bytes belong to the previous blk,
|
---|
| 539 | * no matter what it is used for. Prev blk is guaranteed to
|
---|
| 540 | * (1) Exist (this cannot be 1st blk since 1st is known to
|
---|
| 541 | * start on wd boundary) and to be (2) Non-free (else it would
|
---|
| 542 | * have been merged).
|
---|
| 543 | */
|
---|
| 544 | if((smx = sm->smback) == 0) /* Get ptr to prev blk */
|
---|
| 545 | { sbm_err(0,"Align err"); /* Catch screws */
|
---|
| 546 | return;
|
---|
| 547 | }
|
---|
| 548 | crem = WDSIZE - crem; /* Find # bytes to flush */
|
---|
| 549 | if(crem >= sm->smlen) /* Make sure node has that many */
|
---|
| 550 | { sbm_mmrg(smx); /* Flush node to avoid zero length */
|
---|
| 551 | return;
|
---|
| 552 | }
|
---|
| 553 | smx->smlen += crem; /* Make stray bytes part of prev */
|
---|
| 554 | sm->smaddr += crem; /* And flush from current. */
|
---|
| 555 | sm->smlen -= crem;
|
---|
| 556 | }
|
---|
| 557 | }
|
---|
| 558 | |
---|
| 559 |
|
---|
| 560 | /* SBM_EXP - Expand (or shrink) size of an allocated memory chunk.
|
---|
| 561 | * "nsize" is desired new size; may be larger or smaller than current
|
---|
| 562 | * size.
|
---|
| 563 | */
|
---|
| 564 | struct smblk *
|
---|
| 565 | sbm_exp(sm,size)
|
---|
| 566 | register struct smblk *sm;
|
---|
| 567 | register SBMO size;
|
---|
| 568 | { register struct smblk *smf;
|
---|
| 569 | register SBMO mexp, pred, succ;
|
---|
| 570 |
|
---|
| 571 | if(sm->smlen >= size) /* Do we want truncation? */
|
---|
| 572 | goto realo2; /* Yup, go split block */
|
---|
| 573 |
|
---|
| 574 | /* Block is expanding. */
|
---|
| 575 | mexp = size - sm->smlen; /* Get # bytes to expand by */
|
---|
| 576 | pred = succ = 0;
|
---|
| 577 | if((smf = sm->smforw) /* See if free mem follows */
|
---|
| 578 | && (smf->smflags&(SM_USE|SM_NXM)) == 0)
|
---|
| 579 | if((succ = smf->smlen) >= mexp)
|
---|
| 580 | goto realo1; /* Quick stuff if succ OK */
|
---|
| 581 |
|
---|
| 582 | if((smf = sm->smback) /* See if free mem precedes */
|
---|
| 583 | && (smf->smflags&(SM_USE|SM_NXM)) == 0)
|
---|
| 584 | pred = smf->smlen;
|
---|
| 585 |
|
---|
| 586 | /* If not enough free space combined on both sides of this chunk,
|
---|
| 587 | * we have to look for a completely new block.
|
---|
| 588 | */
|
---|
| 589 | if(pred+succ < mexp)
|
---|
| 590 | { if((smf = sbm_mget(size,size)) == 0)
|
---|
| 591 | return(0); /* Couldn't find one */
|
---|
| 592 | else pred = 0; /* Won, indicate new block */
|
---|
| 593 | }
|
---|
| 594 |
|
---|
| 595 | /* OK, must copy either into new block or down into predecessor
|
---|
| 596 | * (overlap is OK as long as bcopy moves 1st byte first)
|
---|
| 597 | */
|
---|
| 598 | bcopy(sm->smaddr, smf->smaddr, sm->smlen);
|
---|
| 599 | smf->smflags = sm->smflags; /* Copy extra attribs */
|
---|
| 600 | smf->smuse = sm->smuse;
|
---|
| 601 | if(!pred) /* If invoked sbm_mget */
|
---|
| 602 | { sbm_mfree(sm); /* then must free up old area */
|
---|
| 603 | return(smf); /* and can return immediately. */
|
---|
| 604 | }
|
---|
| 605 | sbm_mmrg(smf); /* Merge current into pred blk */
|
---|
| 606 | sm = smf; /* Now pred is current blk. */
|
---|
| 607 |
|
---|
| 608 | if(succ)
|
---|
| 609 | realo1: sbm_mmrg(sm); /* Merge succ into current blk */
|
---|
| 610 | realo2: if(sm->smlen > size /* If now have too much, */
|
---|
| 611 | && sbm_split(sm, size)) /* split up and possibly */
|
---|
| 612 | sbm_mfree(sm->smforw); /* free up unused space. */
|
---|
| 613 | return(sm);
|
---|
| 614 |
|
---|
| 615 | /* Note that sbm_split can fail if it can't get a free node,
|
---|
| 616 | * which is only possible if we are reducing the size of an area.
|
---|
| 617 | * If it fails, we just return anyway without truncating the area.
|
---|
| 618 | */
|
---|
| 619 | }
|
---|
| 620 | |
---|
| 621 |
|
---|
| 622 | /* SBM_MMRG(sm) - Merge a memory area with the area following it.
|
---|
| 623 | * The node (and memory area) following the SM pointed to are
|
---|
| 624 | * merged in and the successor node freed up. The flags
|
---|
| 625 | * and smuse of the current SM (which is not moved or anything)
|
---|
| 626 | * remain the same.
|
---|
| 627 | */
|
---|
| 628 | sbm_mmrg(smp)
|
---|
| 629 | struct smblk *smp;
|
---|
| 630 | { register struct smblk *sm, *sm2;
|
---|
| 631 |
|
---|
| 632 | sm = smp;
|
---|
| 633 | sm->smlen += (sm2 = sm->smforw)->smlen; /* Add succ's len */
|
---|
| 634 | if(sm->smforw = sm2->smforw) /* and fix linkages */
|
---|
| 635 | sm->smforw->smback = sm;
|
---|
| 636 | sbm_nfre(sm2); /* now can flush succ node */
|
---|
| 637 | }
|
---|
| 638 |
|
---|
| 639 | /* SBM_SPLIT - Split up an area (gets a new smblk to point to split-off
|
---|
| 640 | * portion.)
|
---|
| 641 | * Note returned value is ptr to 2nd smblk, since this is a new one.
|
---|
| 642 | * Ptr to 1st remains valid since original smblk stays where it is.
|
---|
| 643 | * NOTE: Beware of splitting up free mem (SM_USE == 0) since sbm_nget may
|
---|
| 644 | * steal it out from under unless precautions are taken! See comments
|
---|
| 645 | * at sbm_mget related to this.
|
---|
| 646 | */
|
---|
| 647 | struct smblk *
|
---|
| 648 | sbm_split(smp,coff)
|
---|
| 649 | struct smblk *smp;
|
---|
| 650 | SBMO coff;
|
---|
| 651 | { register struct smblk *sm, *smx;
|
---|
| 652 | register SBMO csiz;
|
---|
| 653 |
|
---|
| 654 | if((sm = smp)->smlen <= (csiz = coff))
|
---|
| 655 | return(0);
|
---|
| 656 | if((smx = sbm_nget()) == 0)
|
---|
| 657 | return(0);
|
---|
| 658 | smx->smlen = sm->smlen - csiz; /* Set 2nd size */
|
---|
| 659 | smx->smaddr = sm->smaddr + csiz; /* Set 2nd addr */
|
---|
| 660 | sm->smlen = csiz; /* Take from 1st size */
|
---|
| 661 | smx->smflags = sm->smflags; /* Copy flags */
|
---|
| 662 | if(smx->smforw = sm->smforw) /* Splice 2nd after 1 */
|
---|
| 663 | smx->smforw->smback = smx;
|
---|
| 664 | smx->smback = sm;
|
---|
| 665 | sm->smforw = smx; /* Put 2nd into chain */
|
---|
| 666 | return(smx); /* Return ptr to 2nd smblk */
|
---|
| 667 | }
|
---|
| 668 |
|
---|
| 669 | #if 0 /* Replaced by "bcopy" for system-dep efficiency */
|
---|
| 670 | /* SBM_SCPY - Copy string of bytes. Somewhat machine-dependent;
|
---|
| 671 | * Tries to be clever about using word moves instead of byte moves.
|
---|
| 672 | */
|
---|
| 673 | sbm_scpy(from, to, count) /* Copy count bytes from -> to */
|
---|
| 674 | char *from, *to;
|
---|
| 675 | unsigned count;
|
---|
| 676 | { register char *s1, *s2;
|
---|
| 677 | register unsigned cnt;
|
---|
| 678 | int tmp;
|
---|
| 679 |
|
---|
| 680 | if((cnt = count) == 0)
|
---|
| 681 | return;
|
---|
| 682 | s1 = from;
|
---|
| 683 | s2 = to;
|
---|
| 684 | while(rndrem((int)s1)) /* Get 1st ptr aligned */
|
---|
| 685 | { *s2++ = *s1++;
|
---|
| 686 | if(--cnt == 0) return;
|
---|
| 687 | }
|
---|
| 688 | if(rndrem((int)s2) == 0) /* Do wd move if ptr 2 now aligned */
|
---|
| 689 | {
|
---|
| 690 | #ifdef DUMBPCC /* Code for dumber (Portable C type) compiler */
|
---|
| 691 | register WORD *ap, *bp;
|
---|
| 692 | tmp = cnt;
|
---|
| 693 | ap = (WORD *) s1;
|
---|
| 694 | bp = (WORD *) s2;
|
---|
| 695 | if(cnt = rnddiv(cnt))
|
---|
| 696 | do { *bp++ = *ap++; }
|
---|
| 697 | while(--cnt);
|
---|
| 698 | if ((cnt = rndrem(tmp)) ==0)
|
---|
| 699 | return;
|
---|
| 700 | s1 = (char *) ap;
|
---|
| 701 | s2 = (char *) bp;
|
---|
| 702 | #else
|
---|
| 703 | /* Tight loop for efficient copying on 11s */
|
---|
| 704 | tmp = cnt;
|
---|
| 705 | if(cnt = rnddiv(cnt))
|
---|
| 706 | do { *((WORD *)s2)++ = *((WORD *)s1)++; }
|
---|
| 707 | while(--cnt);
|
---|
| 708 | if((cnt = rndrem(tmp)) == 0)
|
---|
| 709 | return;
|
---|
| 710 | #endif /*-DUMBPCC*/
|
---|
| 711 | }
|
---|
| 712 | do { *s2++ = *s1++; } /* Finish up with byte loop */
|
---|
| 713 | while(--cnt);
|
---|
| 714 | }
|
---|
| 715 | #endif /*COMMENT*/
|
---|
| 716 |
|
---|
| 717 | struct smblk * /* If it returns at all, this is most common type */
|
---|
| 718 | sbm_err(val,str,a0,a1,a2,a3)
|
---|
| 719 | char *str;
|
---|
| 720 | struct smblk *val;
|
---|
| 721 | { int *sptr;
|
---|
| 722 |
|
---|
| 723 | sptr = (int *) &sptr; /* Point to self on stack */
|
---|
| 724 | sptr += 5; /* Point to return addr */
|
---|
| 725 | if((int)sbm_debug==1)
|
---|
| 726 | abort();
|
---|
| 727 | if(sbm_debug)
|
---|
| 728 | (*sbm_debug)(0,*sptr,str,a0,a1,a2,a3);
|
---|
| 729 | return(val);
|
---|
| 730 | }
|
---|
| 731 | |
---|
| 732 |
|
---|
| 733 | /* These routines correspond to the V7 LIBC routines as described
|
---|
| 734 | * in the V7 UPM (3). They should provide satisfactory emulation
|
---|
| 735 | * if the documentation is correct. Replacement is necessary since
|
---|
| 736 | * the SBM routines are jealous and cannot tolerate competition for
|
---|
| 737 | * calls of SBRK; i.e. the memory being managed must be contiguous.
|
---|
| 738 | */
|
---|
| 739 |
|
---|
| 740 | /* Guaranteed to return word-aligned pointer to area of AT LEAST
|
---|
| 741 | * requested size. Area size is rounded up to word boundary.
|
---|
| 742 | */
|
---|
| 743 |
|
---|
| 744 | char *
|
---|
| 745 | malloc(size)
|
---|
| 746 | unsigned size;
|
---|
| 747 | { register struct smblk *sm, **sma;
|
---|
| 748 | register SBMO siz;
|
---|
| 749 |
|
---|
| 750 | siz = rndup(size + sizeof (struct smblk *)); /* Make room for ptr */
|
---|
| 751 | if((sm = sbm_mget(siz,siz)) == 0)
|
---|
| 752 | return(0);
|
---|
| 753 | *(sma = (struct smblk **)sm->smaddr) = sm; /* Store ptr in addr-1 */
|
---|
| 754 | return((char *)++sma);
|
---|
| 755 | }
|
---|
| 756 |
|
---|
| 757 | char *
|
---|
| 758 | alloc(size) /* For V6 programs - note different failure value! */
|
---|
| 759 | unsigned size;
|
---|
| 760 | { register char *addr;
|
---|
| 761 | return((addr = malloc(size)) ? addr : (char *) -1);
|
---|
| 762 | }
|
---|
| 763 |
|
---|
| 764 | free(ptr)
|
---|
| 765 | char *ptr;
|
---|
| 766 | { register struct smblk *sm, **smp;
|
---|
| 767 |
|
---|
| 768 | smp = &((struct smblk **)ptr)[-1]; /* Point to addr-1 */
|
---|
| 769 | sm = *smp; /* Pluck SM ptr therefrom */
|
---|
| 770 | if(((sm->smflags&0377) != SM_NID) || sm->smaddr != (SBMA)smp)
|
---|
| 771 | return((int)sbm_err(0,"free: bad arg %o", ptr));
|
---|
| 772 | sbm_mfree(sm);
|
---|
| 773 | return(1);
|
---|
| 774 | }
|
---|
| 775 |
|
---|
| 776 | char *
|
---|
| 777 | realloc(ptr,size)
|
---|
| 778 | char *ptr;
|
---|
| 779 | unsigned size;
|
---|
| 780 | { register struct smblk *sm, **smp;
|
---|
| 781 |
|
---|
| 782 | smp = &((struct smblk **)ptr)[-1]; /* Point to addr-1 */
|
---|
| 783 | sm = *smp; /* Pluck SM ptr therefrom */
|
---|
| 784 | if(((sm->smflags&0377) != SM_NID) || (sm->smaddr != (SBMA)smp))
|
---|
| 785 | return((char *)sbm_err(0,"realloc: bad arg %o",ptr));
|
---|
| 786 | if((sm = sbm_exp(sm, rndup(size+(sizeof(struct smblk *))))) == 0)
|
---|
| 787 | return(0);
|
---|
| 788 | *(smp = (struct smblk **)sm->smaddr) = sm; /* Save smblk ptr */
|
---|
| 789 | return((char *)++smp);
|
---|
| 790 | }
|
---|
| 791 |
|
---|
| 792 | char *
|
---|
| 793 | calloc(nelem,elsize)
|
---|
| 794 | unsigned nelem, elsize;
|
---|
| 795 | { register SBMO cmin;
|
---|
| 796 | register WORD *ip; /* Clear in units of words */
|
---|
| 797 | register char *addr;
|
---|
| 798 |
|
---|
| 799 | if((cmin = nelem*elsize) == 0 /* Find # bytes to get */
|
---|
| 800 | || (addr = malloc(cmin)) == 0) /* Get it */
|
---|
| 801 | return(0);
|
---|
| 802 | ip = (WORD *) addr; /* Set up ptr to area */
|
---|
| 803 | cmin = rnddiv(cmin+WDSIZE-1); /* Find # words to clear */
|
---|
| 804 | do { *ip++ = 0; } while (--cmin); /* Zap the area */
|
---|
| 805 | return(addr);
|
---|
| 806 | }
|
---|
| 807 | |
---|
| 808 |
|
---|
| 809 | /* SBM_NGC() - Specific routine for GC'ing SMBLK nodes.
|
---|
| 810 | *
|
---|
| 811 | * SBM_XNGC(begp, elsize, type) - Compact nodes of specified type.
|
---|
| 812 | * Scans allocated mem from low to high to find chunks with nodes of
|
---|
| 813 | * the specified type.
|
---|
| 814 | * Flushes current freelist and rebuilds it as scan progresses,
|
---|
| 815 | * such that 1st thing on list is lowest-addr node. When a node is
|
---|
| 816 | * seen that can be moved, new node is acquired from freelist if
|
---|
| 817 | * it exists, otherwise no move is made. If a chunk has been scanned
|
---|
| 818 | * and no active nodes remain, it is flushed and freelist updated.
|
---|
| 819 | * NOTE: This has not yet been verified to work with nodes of any
|
---|
| 820 | * type other than SMBLK.
|
---|
| 821 | */
|
---|
| 822 |
|
---|
| 823 | sbm_ngc()
|
---|
| 824 | { register struct smblk *sm;
|
---|
| 825 | if(!(sm = sbm_nxtra))
|
---|
| 826 | return((int)sbm_err(0,"Zero sbm_nxtra"));
|
---|
| 827 | sm->smflags |= SM_USE; /* Ensure this one isn't GC'd */
|
---|
| 828 | sbm_xngc(&sbm_nfl, sizeof(struct smblk), SM_MNODS);
|
---|
| 829 | sm->smflags = 0; /* Flush temporary crock */
|
---|
| 830 | }
|
---|
| 831 | sbm_xngc(begp, elsize, flag)
|
---|
| 832 | struct smblk **begp;
|
---|
| 833 | unsigned elsize, flag;
|
---|
| 834 | { register struct smblk *sm, *chk, *smf;
|
---|
| 835 | struct smblk *ftail, *savtail;
|
---|
| 836 | int cnt, inuse;
|
---|
| 837 |
|
---|
| 838 | *begp = ftail = 0; /* Flush node freelist */
|
---|
| 839 | for(chk = sbm_list; chk; chk = chk->smforw)
|
---|
| 840 | if(chk->smflags&flag)
|
---|
| 841 | { sm = (struct smblk *) chk->smaddr;
|
---|
| 842 | cnt = (chk->smuse)/elsize;
|
---|
| 843 | savtail = ftail;
|
---|
| 844 | inuse = 0;
|
---|
| 845 | smf = *begp;
|
---|
| 846 | /* Set up ptr to 1st freelist node */
|
---|
| 847 | while(--cnt >= 0)
|
---|
| 848 | { /* Here decide if movable */
|
---|
| 849 | if(sm->smflags && smf /* Live and have copy place */
|
---|
| 850 | && (
|
---|
| 851 | (sm->smflags&SM_USE) == 0 /* Free mem? */
|
---|
| 852 | || (sm->smflags&(SM_MNODS|SM_DNODS))
|
---|
| 853 | )
|
---|
| 854 | && sm->smback) /* has backptr (see ncpy) */
|
---|
| 855 | { /* Move the node */
|
---|
| 856 | *begp = smf->smforw; /* Get free node */
|
---|
| 857 | if(smf == ftail)
|
---|
| 858 | ftail = 0;
|
---|
| 859 | if(smf == savtail)
|
---|
| 860 | savtail = 0;
|
---|
| 861 | /* Move node. Already checked for back ptr
|
---|
| 862 | * of 0 since no obvious way to tell where
|
---|
| 863 | * the ptr to list is kept. Sigh.
|
---|
| 864 | */
|
---|
| 865 | sbm_nmov(sm,smf,(struct smblk **)0,elsize);
|
---|
| 866 | /* Get ptr to new freelist node. Note
|
---|
| 867 | * check to ensure that it is not in this
|
---|
| 868 | * same chunk (if it is, no point in moving
|
---|
| 869 | * any nodes!)
|
---|
| 870 | */
|
---|
| 871 | if((smf = *begp) >= chk)
|
---|
| 872 | smf = 0; /* Zero if same chk */
|
---|
| 873 | sm->smflags = 0; /* Make node free */
|
---|
| 874 | }
|
---|
| 875 | /* At this point, not movable */
|
---|
| 876 | if(sm->smflags == 0) /* Free node? */
|
---|
| 877 | { if(ftail) /* Add to freelist */
|
---|
| 878 | ftail->smforw = sm;
|
---|
| 879 | ftail = sm;
|
---|
| 880 | if(*begp == 0)
|
---|
| 881 | *begp = sm;
|
---|
| 882 | sm->smforw = 0;
|
---|
| 883 | }
|
---|
| 884 | else inuse++;
|
---|
| 885 | sm = (struct smblk *)((SBMA)sm + elsize);
|
---|
| 886 | }
|
---|
| 887 | if(inuse == 0 /* All free? */
|
---|
| 888 | && (sm = chk->smback)) /* & not 1st? */
|
---|
| 889 | { if(savtail) /* Edit freelist */
|
---|
| 890 | (ftail = savtail)->smforw = 0;
|
---|
| 891 | else *begp = ftail = 0;
|
---|
| 892 | sbm_mfree(chk);
|
---|
| 893 | chk = sm;
|
---|
| 894 | }
|
---|
| 895 | }
|
---|
| 896 | }
|
---|
| 897 |
|
---|
| 898 | /*
|
---|
| 899 | * Note that proc must return a zero value, or loop aborts and
|
---|
| 900 | * returns that selfsame value.
|
---|
| 901 | */
|
---|
| 902 | sbm_nfor(flag,nodsiz,proc,arg)
|
---|
| 903 | int flag;
|
---|
| 904 | int (*proc)();
|
---|
| 905 | int nodsiz;
|
---|
| 906 | struct sbfile *arg;
|
---|
| 907 | { register struct smblk *sm, *np;
|
---|
| 908 | register int cnt;
|
---|
| 909 | int res;
|
---|
| 910 |
|
---|
| 911 | for(sm = sbm_list; sm; sm = sm->smforw)
|
---|
| 912 | if(sm->smflags&flag)
|
---|
| 913 | { np = (struct smblk *) sm->smaddr;
|
---|
| 914 | cnt = sm->smuse/nodsiz;
|
---|
| 915 | do {
|
---|
| 916 | if(np->smflags)
|
---|
| 917 | if(res = (*proc)(np,arg))
|
---|
| 918 | return(res);
|
---|
| 919 | np = (struct smblk *)((SBMA)np + nodsiz);
|
---|
| 920 | } while(--cnt);
|
---|
| 921 | }
|
---|
| 922 | return(0);
|
---|
| 923 | }
|
---|