minix/commands/elle/sbm.c
2009-11-09 10:26:00 +00:00

915 lines
30 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* SB - Copyright 1982 by Ken Harrenstien, SRI International
* This software is quasi-public; it may be used freely with
* like software, but may NOT be sold or made part of licensed
* products without permission of the author. In all cases
* the source code and any modifications thereto must remain
* available to any user.
*
* This is part of the SB library package.
* Any software using the SB library must likewise be made
* quasi-public, with freely available sources.
*/
#if 0
This file contains the low-level memory allocation
subroutines which are used by the SBLK routines. The code here
is quite machine-dependent, and the definitions in "sb.h" should be
carefully checked to verify that they are correct for the target
machine.
The ultimate low-level routine is "sbrk()" which must be
provided by the system''s C library. SBM expects that successive calls
to sbrk() will return contiguous areas of memory with progressively
higher addresses. Also, the very first call to sbrk() is assumed to
return a word-aligned address.
#endif /*COMMENT*/
#include "sb.h"
#define FUDGE (sizeof(struct smblk)) /* Allow this much fudge in
allocation, to prevent undue fragmentation */
char *(*sbm_debug)(); /* Debug switch - user-furnished routine */
struct smblk *sbm_nfl; /* Pointer to node freelist */
struct smblk *sbm_nxtra; /* Reserved extra free node */
struct smblk *sbm_list; /* Pointer to smblk memory alloc list.
* ALL smblks are strung onto this list
* except for the freelist!
*/
SBMA sbm_lowaddr; /* Lowest word-aligned address we know about.*/
/* If compiling with debug switch set, use special routine in place of
* sbrk so we can pretend we have a very limited area of free memory.
*/
#ifdef DBG_SIZE
#define SBM_SBRK sbm_brk
char *sbm_brk();
#else
#define SBM_SBRK sbrk
#endif /*DBG_SIZE*/
/* Forward routine declarations */
char *sbrk();
struct smblk *sbm_nmak(), *sbm_nget(), *sbm_mget(), *sbm_split();
struct smblk *sbm_lmak(), *sbm_err();
/* SBM_INIT - Initialize storage management.
* If args are zero, normal initialization is done. Otherwise,
* args are understood to be pointers to an area of memory allocated
* on the stack (eg by an "int mem[2000]" declaration in MAIN) and
* initialization will include this area in addition to the
* unused space between "_end" and the start of the stack segment.
* This is mostly of use for PDP11s which would otherwise waste a lot
* of address space.
* Maybe should have a SBM_RESET() function?
*/
struct smblk *
sbm_init(xaddr,xlen)
SBMA xaddr; /* Address of allocated stack area if any */
SBMO xlen; /* Size of this area */
{ register struct smblk *sm, *sml;
register char *cp;
/* Get initial chunk of memory from standard system rtn */
if((cp = SBM_SBRK(SMNODES*sizeof(struct smblk))) == 0
|| (int) cp == -1)
return(sbm_err(0,"Can't sbrk"));
sm = (struct smblk *)cp; /* Better be word-aligned! */
sbm_lmak(sm,(SBMO)sizeof(struct smblk),SMNODES); /* Make list */
sbm_nfl = sm; /* Point freelist at it */
sbm_lowaddr = (SBMA)sm; /* Remember lowest addr seen */
/* Set up 1st node pointing to all memory from here on up.
* We don't know exactly how much will be available at this point,
* so we just pretend we have the maximum possible.
*/
sbm_list = sml = sbm_nget();
sml->smforw = sml->smback = 0;
sml->smflags = SM_USE|SM_NID; /* Initial flags */
sml->smaddr = (SBMA) sml;
sml->smlen = MAXSBMO; /* Pretend we have lots */
sml->smuse = (SMNODES * sizeof(struct smblk));
/* Now split off everything above initial allocation as NXM. */
sm = sbm_split(sml, sm->smuse);
sml->smflags |= SM_MNODS; /* Mark 1st node as having SM nodes */
sm->smflags |= SM_NXM; /* Mark 2nd node as NXM */
/* Now possibly set up extra nodes, if stack mem is being allocated
* (From our viewpoint it looks as if a chunk in the middle of
* the initial NXM section has been declared usable)
*/
if(xlen)
{ /* Allow for "extra" static stack memory */
/* Will lose if xaddr <= 1st NXM! */
sml = sbm_split(sm, (SBMO)(xaddr - sm->smaddr));
sbm_split(sml, xlen); /* Split off following NXM */
sml->smflags &= ~(SM_USE|SM_NXM); /* This node is free mem! */
}
/* Now set up a small additional node which points to the NXM
* that we cannot get from SBRK. At this stage, this is just
* a place-holder, to reserve the node so we don't have to
* worry about running out of nodes at the same time sbrk stops
* returning memory.
* SM points to the NXM that we expect SBRK to dig into.
*/
sbm_split(sm, sm->smlen - WDSIZE); /* Chop off teensy bit */
sm->smflags &= ~SM_USE; /* Now mark NXM "free"!! */
/* Finally, reserve an "extra" SM node for use by sbm_nget
* when it is allocating more freelist node chunks.
*/
sbm_nxtra = sbm_nget();
return(sbm_list);
}
/* SBM_NGET() - Get a free SM node.
* Note the hair to provide a spare SM node when
* we are allocating memory for more SM nodes. This is necessary
* because sbm_mget and sbm_nget call each other recursively and
* sbm_mget cannot create any new memory without a SM node to point
* at the allocated chunk.
*/
struct smblk *
sbm_nget()
{ register struct smblk *sm, *sml;
if(!(sm = sbm_nfl)) /* Get a node from freelist */
{ /* Freelist is empty, try to allocate more nodes. */
/* Put our "spare" smblk on freelist temporarily so that
* sbm_mget has a chance of winning.
* Infinite recursion is avoided by a test
* in sbm_mget which checks sbm_nfl and sbm_nxtra.
*/
if(!(sm = sbm_nxtra))
return(sbm_err(0,"Zero sbm_nxtra!"));
sm->smforw = 0;
sbm_nfl = sm;
sbm_nxtra = 0;
/* Try to allocate another chunk of SM nodes. */
sml = sbm_nmak(sizeof(struct smblk),SM_MNODS);
/* Put the new free nodes (if any) on freelist.
* Done this way because freelist may have had one or two
* nodes added to it by sbm_mget, so can't just stick
* a new pointer in sbm_nfl.
*/
while(sm = sml)
{ sml = sm->smforw;
sbm_nfre(sm);
}
/* Now reserve an extra node again.
* It is an error if there is nothing on freelist here,
* because even if sbm_mget failed the "extra node" should
* still be on freelist. The check for a zero sbm_nxtra
* above will catch such an error.
*/
sbm_nxtra = sbm_nget();
/* Now see if anything to return */
if(!(sm = sbm_nfl)) /* If freelist empty again, */
return(0); /* give up. */
}
sbm_nfl = sm->smforw; /* If win, take it off freelist */
return(sm); /* Return ptr or 0 if none */
}
/* SBM_NFRE(sm) - Return a SM node to the SM freelist.
*/
sbm_nfre(smp)
struct smblk *smp;
{ register struct smblk *sm;
(sm = smp)->smflags = 0;
sm->smforw = sbm_nfl;
sbm_nfl = sm;
}
/* SBM_NMAK(elsize, flag) - Make (allocate & build) a typeless node freelist.
*/
struct smblk *
sbm_nmak(elsize, flag)
SBMO elsize;
unsigned flag;
{ register struct smblk *sm, *smp;
register int cnt;
if((sm = sbm_mget(SMNODES*elsize,SMNODES*elsize)) == 0)
return(0);
sm->smflags |= flag; /* Indicate type of nodes */
cnt = sm->smlen/elsize; /* Find # nodes that will fit */
sm->smuse = cnt * elsize; /* Actual size used */
smp = (struct smblk *)(sm->smaddr); /* Ptr to 1st loc of mem */
sbm_lmak(smp, (SBMO)elsize, cnt); /* Build freelist */
return(smp); /* Return 1st free node. Caller is */
/* responsible for setting freelist ptr. */
}
/* SBM_LMAK - Build freelist of typeless nodes.
* Note this does not allocate memory, it just converts an already
* allocated memory area.
*/
struct smblk *
sbm_lmak(addr, elsize, num)
SBMA addr;
SBMO elsize;
int num;
{ register struct smblk *sm, *smp;
register int cnt;
smp = (struct smblk *) addr;
if((cnt = num) <= 0)
return(0);
do { sm = smp; /* Save ptr */
sm->smforw = (smp = (struct smblk *) ((SBMA)smp + elsize));
sm->smflags = 0;
} while(--cnt);
sm->smforw = 0; /* Last node points to nothing */
return(sm); /* Return ptr to last node */
}
/* SBM_NMOV(sm1, sm2, begp, elsize) - Move a typeless node.
* Copy sm1 to sm2, adjust ptrs, leave sm1 free.
*/
sbm_nmov(smp1,smp2,begp,elsize)
struct smblk *smp1, *smp2, **begp;
int elsize;
{ register struct smblk *sm;
bcopy((SBMA)smp1,(SBMA)(sm = smp2), elsize); /* Copy the stuff */
if(sm->smforw) sm->smforw->smback = sm; /* Fix up links */
if(sm->smback) sm->smback->smforw = sm;
else *begp = sm;
}
/* SBM_MGET(min,max) - Get a SMBLK with specified amount of memory.
* Returns 0 if none available.
* Memory is guaranteed to start on word boundary, but may not
* end on one. Note that sbm_mfree is responsible for
* ensuring that free mem starts word-aligned.
* A subtle but major concern of this code is the number of freelist
* nodes gobbled by a single call. If the freelist happens to not have
* enough nodes, then a recursive call to sbm_mget is made (via sbm_nget)
* in order to allocate a new batch of freelist nodes! sbm_nget will
* always provide a single "spare" node during such an allocation, but
* there is only one and it is essential that sbm_mget gobble only ONE
* (if any) during such a call, which is indicated by sbm_nxtra==0.
* The maximum # of freelist nodes that sbm_mget can gobble is
* 2, when (1) NXM memory is obtained, and a SM is needed to point at
* the new free mem, plus (2) the resulting SM is too big, and has to
* be split up, which requires another SM for the remainder.
* The "used-NXM" smblk is set up at init time precisely in order to
* avoid the necessity of creating it here when sbrk stops winning, since
* that would require yet another freelist node and make it possible for
* sbm_mget to gobble 3 during one call -- too many.
* Further note: the sbm_nfl checks are necessary in order
* to ensure that a SM node is available for use by sbm_split. Otherwise
* the calls to sbm_split might create a new SM freelist by gobbling the
* very memory which we are hoping to return!
*/
SBMO sbm_chksiz = SMCHUNKSIZ; /* Current chunk size to feed sbrk */
struct smblk *
sbm_mget(cmin,cmax)
SBMO cmin,cmax;
{ register struct smblk *sm, *sml;
register SBMO csiz;
register SBMA addr, xaddr;
if((sm = sbm_list) == 0 /* If never done, */
&& (sm = sbm_init((SBMA)0,(SBMO)0)) == 0) /* initialize mem alloc stuff. */
return(0); /* Can't init??? */
/* Round up sizes to word boundary */
if(rndrem(cmin)) cmin = rndup(cmin);
if(rndrem(cmax)) cmax = rndup(cmax);
/* Search for a free block having enough memory.
* If run into a free-NXM block, always "win", since there may be
* a combination of preceding free-mem and new mem which will satisfy
* the request. If it turns out this didn't work, we'll just fail
* a little farther on.
*/
retry: csiz = cmin; /* Set size that will satisfy us */
do {
if( ((sm->smflags&SM_USE) == 0)
&& ((sm->smlen >= csiz) || (sm->smflags&SM_NXM)) )
break;
} while(sm = sm->smforw);
if(sm == 0)
return(0); /* Found none that minimum would fit */
if(sm->smflags&SM_NXM)
{ /* Found free area, but it's marked NXM and the system
* must be persuaded (via sbrk) to let us use that portion
* of our address space. Grab a good-sized chunk.
*/
if(sbm_nfl == 0) /* Verify a spare SM node is avail */
goto getnod; /* Nope, must get one. */
/* Decide amount of mem to ask system for, via sbrk.
* The fine point here is the check of sbm_nxtra to make sure
* that, when building more freelist nodes, we don't have
* to use more than one SM node in the process. If we
* asked for too much mem, we'd have to use a SM node
* to hold the excess after splitting.
*/
csiz = cmax;
if(sbm_nxtra /* If normal then try for big chunk */
&& csiz < sbm_chksiz) csiz = sbm_chksiz; /* Max */
if (csiz > sm->smlen) csiz = sm->smlen; /* Min */
/* Get the NXM mem */
if((addr = (SBMA)SBM_SBRK(csiz)) != sm->smaddr)
{ /* Unexpected value returned from SBRK! */
if((int)addr != 0 && (int)addr != -1)
{ return(sbm_err(0,"SBRK %o != %o", addr,
sm->smaddr));
#if 0
/* If value indicates couldn't get the stuff, then
* we have probably hit our limit and the rest of
* NXM should be declared "used" to prevent further
* hopeless sbrk calls. We split off the portion
* of NXM that is known for sure to be unavailable,
* and mark it "used". If a "used NXM" area already
* exists following this one, the two are merged.
* The chunk size is then reduced by half, so
* only log2(SMCHUNKSIZ) attempts will be made, and
* we try again.
*/
/* If returned some mem which starts outside
* the NXM then something is screwed up. */
if(addr < sm->smaddr
|| (addr >= sm->smaddr+sm->smlen))
return(sbm_err(0,"SBRK %o != %o",
addr, sm->smaddr));
/* Got some mem, falls within NXM.
* Presumably someone else has called sbrk
* since last time, so we need to fence off
* the intervening area. */
sm = sbm_split((sml=sm),(addr - sm->smaddr));
sml->smflags |= SM_USE|SM_EXT;
return(sbm_mget(cmin,cmax));
#endif /*COMMENT*/
}
/* Handle case of SBRK claiming no more memory.
* Gobble as much as we can, and then turn this NXM
* block into a free-mem block, and leave the
* remainder in the used-NXM block (which should
* immediately follow this free-NXM block!)
*/
if(!(sml = sm->smforw) /* Ensure have used-NXM blk */
|| (sml->smflags&(SM_USE|SM_NXM))
!= (SM_USE|SM_NXM))
return(sbm_err(0,"No uNXM node!"));
xaddr = sm->smaddr; /* Use this for checking */
sm->smuse = 0; /* Use this for sum */
for(csiz = sm->smlen; csiz > 0;)
{ addr = SBM_SBRK(csiz);
if((int)addr == 0 || (int)addr == -1)
{ csiz >>= 1;
continue;
}
if(addr != xaddr)
return(sbm_err(0,"SBRK %o != %o", addr,
xaddr));
sm->smuse += csiz;
xaddr += csiz;
}
/* Have gobbled as much from SBRK as we could.
* Turn the free-NXM block into a free-mem block,
* unless we got nothing, in which case just merge
* it into the used-NXM block and continue
* searching from this point.
*/
if(!(csiz = sm->smuse)) /* Get total added */
{ sm->smflags = sml->smflags; /* Ugh. */
sbm_mmrg(sm);
goto retry; /* Keep looking */
}
else
{ sml->smaddr = sm->smaddr + csiz;
sml->smlen += sm->smlen - csiz;
sm->smlen = csiz;
sm->smflags &= ~SM_NXM; /* No longer NXM */
}
}
/* Here when we've acquired CSIZ more memory from sbrk.
* If preceding mem area is not in use, merge new mem
* into it.
*/
if((sml = sm->smback) &&
(sml->smflags&(SM_USE|SM_NXM))==0) /* Previous free? */
{ sml->smlen += csiz; /* Yes, simple! */
sm->smaddr += csiz; /* Fix up */
if((sm->smlen -= csiz) == 0) /* If no NXM left,*/
sbm_mmrg(sml); /* Merge NXM node w/prev */
sm = sml; /* Prev is now winning node */
}
else
{ /* Prev node isn't a free area. Split up the NXM
* node to account for acquired mem, unless we
* gobbled all the mem available.
*/
if(sm->smlen > csiz /* Split unless all used */
&& !sbm_split(sm,csiz)) /* Call shd always win */
return(sbm_err(0,"getsplit err: %o",sm));
sm->smflags &= ~SM_NXM; /* Node is now real mem */
}
/* Now make a final check that we have enough memory.
* This can fail because SBRK may not have been able
* to gobble enough memory, either because (1) not
* as much NXM was available as we thought,
* or (2) we noticed the free-NXM area and immediately
* gambled on trying it without checking any lengths.
* In any case, we try again starting from the current SM
* because there may be more free mem higher up (eg on
* stack).
*/
if(sm->smlen < cmin)
goto retry;
}
/* Check to see if node has too much mem. This is especially true
* for memory just acquired via sbrk, which gobbles a huge chunk each
* time. If there's too much, we split up the area.
*/
if(sm->smlen > cmax+FUDGE) /* Got too much? (Allow some fudge)*/
/* Yes, split up so don't gobble too much. */
if(sbm_nfl) /* If success guaranteed, */
sbm_split(sm,cmax); /* split it, all's well. */
else goto getnod;
sm->smuse = 0;
sm->smflags |= SM_USE; /* Finally seize it by marking "in-use". */
return(sm);
/* Come here when we will need to get another SM node but the
* SM freelist is empty. We have to forget about using the area
* we just found, because sbm_nget may gobble it for the
* freelist. So, we first force a refill of the freelist, and then
* invoke ourselves again on what's left.
*/
getnod:
if(sml = sbm_nget()) /* Try to build freelist */
{ sbm_nfre(sml); /* Won, give node back, */
sm = sbm_list; /* and retry, starting over! */
goto retry;
}
/* Failed. Not enough memory for both this request
* and one more block of SM nodes. Since such a SM_MNODS
* block isn't very big, we are so close to the limits that it
* isn't worth trying to do something fancy here to satisfy the
* original request. So we just fail.
*/
return(0);
}
#ifdef DBG_SIZE
/* Code for debugging stuff by imposing an artificial limitation on size
* of available memory.
*/
SBMO sbm_dlim = MAXSBMO; /* Amount of mem to allow (default is max) */
char *
sbm_brk(size)
unsigned size;
{ register char *addr;
if(size > sbm_dlim) return(0);
addr = sbrk(size);
if((int)addr == 0 || (int)addr == -1)
return(0);
sbm_dlim -= size;
return(addr);
}
#endif /*DBG_SIZE*/
/* SBM_MFREE(sm) - Free up an allocated memory area.
*/
sbm_mfree(sm)
register struct smblk *sm;
{ register struct smblk *smx;
register SBMO crem;
sm->smflags &= ~SM_USE; /* Say mem is free */
if((smx = sm->smback) /* Check preceding mem */
&& (smx->smflags&(SM_USE|SM_NXM))==0) /* If it's free, */
sbm_mmrg(sm = smx); /* then merge 'em. */
if((smx = sm->smforw) /* Check following mem */
&& (smx->smflags&(SM_USE|SM_NXM))==0) /* Again, if free, */
sbm_mmrg(sm); /* merge them. */
if(sm->smlen == 0) /* Just in case, chk for null blk */
{ if(smx = sm->smback) /* If pred exists, */
sbm_mmrg(smx); /* merge quietly. */
else {
sbm_list = sm->smforw; /* 1st node on list, so */
sbm_nfre(sm); /* simply flush it. */
}
return;
}
/* This code is slightly over-general for some machines.
* The pointer subtraction is done in order to get a valid integer
* offset value regardless of the internal representation of a pointer.
* We cannot reliably force alignment via casts; some C implementations
* treat that as a no-op.
*/
if(crem = rndrem(sm->smaddr - sbm_lowaddr)) /* On word bndry? */
{ /* No -- must adjust. All free mem blks MUST, by fiat,
* start on word boundary. Here we fix things by
* making the leftover bytes belong to the previous blk,
* no matter what it is used for. Prev blk is guaranteed to
* (1) Exist (this cannot be 1st blk since 1st is known to
* start on wd boundary) and to be (2) Non-free (else it would
* have been merged).
*/
if((smx = sm->smback) == 0) /* Get ptr to prev blk */
{ sbm_err(0,"Align err"); /* Catch screws */
return;
}
crem = WDSIZE - crem; /* Find # bytes to flush */
if(crem >= sm->smlen) /* Make sure node has that many */
{ sbm_mmrg(smx); /* Flush node to avoid zero length */
return;
}
smx->smlen += crem; /* Make stray bytes part of prev */
sm->smaddr += crem; /* And flush from current. */
sm->smlen -= crem;
}
}
/* SBM_EXP - Expand (or shrink) size of an allocated memory chunk.
* "nsize" is desired new size; may be larger or smaller than current
* size.
*/
struct smblk *
sbm_exp(sm,size)
register struct smblk *sm;
register SBMO size;
{ register struct smblk *smf;
register SBMO mexp, pred, succ;
if(sm->smlen >= size) /* Do we want truncation? */
goto realo2; /* Yup, go split block */
/* Block is expanding. */
mexp = size - sm->smlen; /* Get # bytes to expand by */
pred = succ = 0;
if((smf = sm->smforw) /* See if free mem follows */
&& (smf->smflags&(SM_USE|SM_NXM)) == 0)
if((succ = smf->smlen) >= mexp)
goto realo1; /* Quick stuff if succ OK */
if((smf = sm->smback) /* See if free mem precedes */
&& (smf->smflags&(SM_USE|SM_NXM)) == 0)
pred = smf->smlen;
/* If not enough free space combined on both sides of this chunk,
* we have to look for a completely new block.
*/
if(pred+succ < mexp)
{ if((smf = sbm_mget(size,size)) == 0)
return(0); /* Couldn't find one */
else pred = 0; /* Won, indicate new block */
}
/* OK, must copy either into new block or down into predecessor
* (overlap is OK as long as bcopy moves 1st byte first)
*/
bcopy(sm->smaddr, smf->smaddr, sm->smlen);
smf->smflags = sm->smflags; /* Copy extra attribs */
smf->smuse = sm->smuse;
if(!pred) /* If invoked sbm_mget */
{ sbm_mfree(sm); /* then must free up old area */
return(smf); /* and can return immediately. */
}
sbm_mmrg(smf); /* Merge current into pred blk */
sm = smf; /* Now pred is current blk. */
if(succ)
realo1: sbm_mmrg(sm); /* Merge succ into current blk */
realo2: if(sm->smlen > size /* If now have too much, */
&& sbm_split(sm, size)) /* split up and possibly */
sbm_mfree(sm->smforw); /* free up unused space. */
return(sm);
/* Note that sbm_split can fail if it can't get a free node,
* which is only possible if we are reducing the size of an area.
* If it fails, we just return anyway without truncating the area.
*/
}
/* SBM_MMRG(sm) - Merge a memory area with the area following it.
* The node (and memory area) following the SM pointed to are
* merged in and the successor node freed up. The flags
* and smuse of the current SM (which is not moved or anything)
* remain the same.
*/
sbm_mmrg(smp)
struct smblk *smp;
{ register struct smblk *sm, *sm2;
sm = smp;
sm->smlen += (sm2 = sm->smforw)->smlen; /* Add succ's len */
if(sm->smforw = sm2->smforw) /* and fix linkages */
sm->smforw->smback = sm;
sbm_nfre(sm2); /* now can flush succ node */
}
/* SBM_SPLIT - Split up an area (gets a new smblk to point to split-off
* portion.)
* Note returned value is ptr to 2nd smblk, since this is a new one.
* Ptr to 1st remains valid since original smblk stays where it is.
* NOTE: Beware of splitting up free mem (SM_USE == 0) since sbm_nget may
* steal it out from under unless precautions are taken! See comments
* at sbm_mget related to this.
*/
struct smblk *
sbm_split(smp,coff)
struct smblk *smp;
SBMO coff;
{ register struct smblk *sm, *smx;
register SBMO csiz;
if((sm = smp)->smlen <= (csiz = coff))
return(0);
if((smx = sbm_nget()) == 0)
return(0);
smx->smlen = sm->smlen - csiz; /* Set 2nd size */
smx->smaddr = sm->smaddr + csiz; /* Set 2nd addr */
sm->smlen = csiz; /* Take from 1st size */
smx->smflags = sm->smflags; /* Copy flags */
if(smx->smforw = sm->smforw) /* Splice 2nd after 1 */
smx->smforw->smback = smx;
smx->smback = sm;
sm->smforw = smx; /* Put 2nd into chain */
return(smx); /* Return ptr to 2nd smblk */
}
#if 0 /* Replaced by "bcopy" for system-dep efficiency */
/* SBM_SCPY - Copy string of bytes. Somewhat machine-dependent;
* Tries to be clever about using word moves instead of byte moves.
*/
sbm_scpy(from, to, count) /* Copy count bytes from -> to */
char *from, *to;
unsigned count;
{ register char *s1, *s2;
register unsigned cnt;
int tmp;
if((cnt = count) == 0)
return;
s1 = from;
s2 = to;
while(rndrem((int)s1)) /* Get 1st ptr aligned */
{ *s2++ = *s1++;
if(--cnt == 0) return;
}
if(rndrem((int)s2) == 0) /* Do wd move if ptr 2 now aligned */
{
#ifdef DUMBPCC /* Code for dumber (Portable C type) compiler */
register WORD *ap, *bp;
tmp = cnt;
ap = (WORD *) s1;
bp = (WORD *) s2;
if(cnt = rnddiv(cnt))
do { *bp++ = *ap++; }
while(--cnt);
if ((cnt = rndrem(tmp)) ==0)
return;
s1 = (char *) ap;
s2 = (char *) bp;
#else
/* Tight loop for efficient copying on 11s */
tmp = cnt;
if(cnt = rnddiv(cnt))
do { *((WORD *)s2)++ = *((WORD *)s1)++; }
while(--cnt);
if((cnt = rndrem(tmp)) == 0)
return;
#endif /*-DUMBPCC*/
}
do { *s2++ = *s1++; } /* Finish up with byte loop */
while(--cnt);
}
#endif /*COMMENT*/
struct smblk * /* If it returns at all, this is most common type */
sbm_err(val,str,a0,a1,a2,a3)
char *str;
struct smblk *val;
{ int *sptr;
sptr = (int *) &sptr; /* Point to self on stack */
sptr += 5; /* Point to return addr */
if((int)sbm_debug==1)
abort();
if(sbm_debug)
(*sbm_debug)(0,*sptr,str,a0,a1,a2,a3);
return(val);
}
/* These routines correspond to the V7 LIBC routines as described
* in the V7 UPM (3). They should provide satisfactory emulation
* if the documentation is correct. Replacement is necessary since
* the SBM routines are jealous and cannot tolerate competition for
* calls of SBRK; i.e. the memory being managed must be contiguous.
*/
/* Guaranteed to return word-aligned pointer to area of AT LEAST
* requested size. Area size is rounded up to word boundary.
*/
char *
malloc(size)
unsigned size;
{ register struct smblk *sm, **sma;
register SBMO siz;
siz = rndup(size + sizeof (struct smblk *)); /* Make room for ptr */
if((sm = sbm_mget(siz,siz)) == 0)
return(0);
*(sma = (struct smblk **)sm->smaddr) = sm; /* Store ptr in addr-1 */
return((char *)++sma);
}
char *
alloc(size) /* For V6 programs - note different failure value! */
unsigned size;
{ register char *addr;
return((addr = malloc(size)) ? addr : (char *) -1);
}
free(ptr)
char *ptr;
{ register struct smblk *sm, **smp;
smp = &((struct smblk **)ptr)[-1]; /* Point to addr-1 */
sm = *smp; /* Pluck SM ptr therefrom */
if(((sm->smflags&0377) != SM_NID) || sm->smaddr != (SBMA)smp)
return((int)sbm_err(0,"free: bad arg %o", ptr));
sbm_mfree(sm);
return(1);
}
char *
realloc(ptr,size)
char *ptr;
unsigned size;
{ register struct smblk *sm, **smp;
smp = &((struct smblk **)ptr)[-1]; /* Point to addr-1 */
sm = *smp; /* Pluck SM ptr therefrom */
if(((sm->smflags&0377) != SM_NID) || (sm->smaddr != (SBMA)smp))
return((char *)sbm_err(0,"realloc: bad arg %o",ptr));
if((sm = sbm_exp(sm, rndup(size+(sizeof(struct smblk *))))) == 0)
return(0);
*(smp = (struct smblk **)sm->smaddr) = sm; /* Save smblk ptr */
return((char *)++smp);
}
char *
calloc(nelem,elsize)
unsigned nelem, elsize;
{ register SBMO cmin;
register WORD *ip; /* Clear in units of words */
register char *addr;
if((cmin = nelem*elsize) == 0 /* Find # bytes to get */
|| (addr = malloc(cmin)) == 0) /* Get it */
return(0);
ip = (WORD *) addr; /* Set up ptr to area */
cmin = rnddiv(cmin+WDSIZE-1); /* Find # words to clear */
do { *ip++ = 0; } while (--cmin); /* Zap the area */
return(addr);
}
/* SBM_NGC() - Specific routine for GC'ing SMBLK nodes.
*
* SBM_XNGC(begp, elsize, type) - Compact nodes of specified type.
* Scans allocated mem from low to high to find chunks with nodes of
* the specified type.
* Flushes current freelist and rebuilds it as scan progresses,
* such that 1st thing on list is lowest-addr node. When a node is
* seen that can be moved, new node is acquired from freelist if
* it exists, otherwise no move is made. If a chunk has been scanned
* and no active nodes remain, it is flushed and freelist updated.
* NOTE: This has not yet been verified to work with nodes of any
* type other than SMBLK.
*/
sbm_ngc()
{ register struct smblk *sm;
if(!(sm = sbm_nxtra))
return((int)sbm_err(0,"Zero sbm_nxtra"));
sm->smflags |= SM_USE; /* Ensure this one isn't GC'd */
sbm_xngc(&sbm_nfl, sizeof(struct smblk), SM_MNODS);
sm->smflags = 0; /* Flush temporary crock */
}
sbm_xngc(begp, elsize, flag)
struct smblk **begp;
unsigned elsize, flag;
{ register struct smblk *sm, *chk, *smf;
struct smblk *ftail, *savtail;
int cnt, inuse;
*begp = ftail = 0; /* Flush node freelist */
for(chk = sbm_list; chk; chk = chk->smforw)
if(chk->smflags&flag)
{ sm = (struct smblk *) chk->smaddr;
cnt = (chk->smuse)/elsize;
savtail = ftail;
inuse = 0;
smf = *begp;
/* Set up ptr to 1st freelist node */
while(--cnt >= 0)
{ /* Here decide if movable */
if(sm->smflags && smf /* Live and have copy place */
&& (
(sm->smflags&SM_USE) == 0 /* Free mem? */
|| (sm->smflags&(SM_MNODS|SM_DNODS))
)
&& sm->smback) /* has backptr (see ncpy) */
{ /* Move the node */
*begp = smf->smforw; /* Get free node */
if(smf == ftail)
ftail = 0;
if(smf == savtail)
savtail = 0;
/* Move node. Already checked for back ptr
* of 0 since no obvious way to tell where
* the ptr to list is kept. Sigh.
*/
sbm_nmov(sm,smf,(struct smblk **)0,elsize);
/* Get ptr to new freelist node. Note
* check to ensure that it is not in this
* same chunk (if it is, no point in moving
* any nodes!)
*/
if((smf = *begp) >= chk)
smf = 0; /* Zero if same chk */
sm->smflags = 0; /* Make node free */
}
/* At this point, not movable */
if(sm->smflags == 0) /* Free node? */
{ if(ftail) /* Add to freelist */
ftail->smforw = sm;
ftail = sm;
if(*begp == 0)
*begp = sm;
sm->smforw = 0;
}
else inuse++;
sm = (struct smblk *)((SBMA)sm + elsize);
}
if(inuse == 0 /* All free? */
&& (sm = chk->smback)) /* & not 1st? */
{ if(savtail) /* Edit freelist */
(ftail = savtail)->smforw = 0;
else *begp = ftail = 0;
sbm_mfree(chk);
chk = sm;
}
}
}
/*
* Note that proc must return a zero value, or loop aborts and
* returns that selfsame value.
*/
sbm_nfor(flag,nodsiz,proc,arg)
int flag;
int (*proc)();
int nodsiz;
struct sbfile *arg;
{ register struct smblk *sm, *np;
register int cnt;
int res;
for(sm = sbm_list; sm; sm = sm->smforw)
if(sm->smflags&flag)
{ np = (struct smblk *) sm->smaddr;
cnt = sm->smuse/nodsiz;
do {
if(np->smflags)
if(res = (*proc)(np,arg))
return(res);
np = (struct smblk *)((SBMA)np + nodsiz);
} while(--cnt);
}
return(0);
}