|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: T a
Length: 12218 (0x2fba) Types: TextFile Names: »allocate.c«
└─⟦9ae75bfbd⟧ Bits:30007242 EUUGD3: Starter Kit └─⟦e7f64e0c0⟧ »EurOpenD3/mail/vmh.tar.Z« └─⟦dcb95597f⟧ └─⟦this⟧ »allocate.c«
#ifndef lint static char rcsid[] = "$Header: allocate.c,v 2.9 88/01/13 18:45:34 deboor Exp $"; static char notice[] = "This program is in the public domain and is available for unlimited \ distribution as long as this notice is enclosed."; #endif /* ** for memory allocation with single punt() calls! ** ** $Source: /c/support/deboor/usr/src/old/vmh/RCS/allocate.c,v $ ** $Revision: 2.9 $ ** $Author: deboor $ ** ** FUNCTIONS: ** _Calloc front end for calloc() -- no NULL returns ** _Free non-useful front-end for free() ** _Malloc front-end for malloc() -- no NULL returns ** abortcatch non-useful abort() handler [abort won't be handled] ** free signal-blocking free() routine ** malloc signal-blocking malloc() routine ** newstr return copy of passed-in string ** realloc signal-blocking reallocation routine */ # include "vmh.h" char *calloc(); /* ** the following is taken from the standard 4.3 C library. I need ** to have it to be able to block signals when people like curses ** and stdio are using it. sigh. */ /* * Copyright (c) 1980 Regents of the University of California. * All rights reserved. The Berkeley software License Agreement * specifies the terms and conditions for redistribution. */ /* * malloc.c (Caltech) 2/21/82 * Chris Kingsley, kingsley@cit-20. * * This is a very fast storage allocator. It allocates blocks of a small * number of different sizes, and keeps free lists of each size. Blocks that * don't exactly fit are passed up to the next larger size. In this * implementation, the available sizes are 2^n-4 (or 2^n-12) bytes long. * This is designed for use in a program that uses vast quantities of memory, * but bombs when it runs out. */ #define NULL 0 /* * The overhead on a block is at least 4 bytes. When free, this space * contains a pointer to the next free block, and the bottom two bits must * be zero. When in use, the first byte is set to MAGIC, and the second * byte is the size index. The remaining bytes are for alignment. * If range checking is enabled and the size of the block fits * in two bytes, then the top two bytes hold the size of the requested block * plus the range checking words, and the header word MINUS ONE. */ union overhead { union overhead *ov_next; /* when free */ struct { #ifndef RCHECK u_char ovu_magic; /* magic number */ u_char ovu_index; /* bucket # */ #else u_int ovu_size; /* actual block size */ u_char ovu_magic; /* magic number */ u_char ovu_index; /* bucket # */ u_short ovu_rmagic; /* range magic number */ #endif } ovu; #define ov_magic ovu.ovu_magic #define ov_index ovu.ovu_index #define ov_rmagic ovu.ovu_rmagic #define ov_size ovu.ovu_size }; #define MAGIC 0xef /* magic # on accounting info */ #define RMAGIC 0x5555 /* magic # on range info */ #ifdef RCHECK #define RSLOP sizeof (u_short) #else #define RSLOP 0 #endif /* * nextf[i] is the pointer to the next free block of size 2^(i+3). The * smallest allocatable block is 8 bytes. The overhead information * precedes the data area returned to the user. */ #define NBUCKETS 30 static union overhead *nextf[NBUCKETS]; extern char *sbrk(); static int pagesz; /* page size */ static int pagebucket; /* page size bucket */ #ifdef MSTATS /* * nmalloc[i] is the difference between the number of mallocs and frees * for a given block size. */ static u_int nmalloc[NBUCKETS]; #include <stdio.h> #endif #ifdef DEBUG #define ASSERT(p) if (!(p)) botch("p") static botch(s) char *s; { printf("assertion botched: %s\n", s); abort(); } #else #define ASSERT(p) #endif char * malloc(nbytes) unsigned nbytes; { register union overhead *op; register int bucket; register unsigned amt, n; char *sbrk(); blocksig(); /* * First time malloc is called, setup page size and * align break pointer so all data will be page aligned. */ if (pagesz == 0) { pagesz = n = getpagesize(); op = (union overhead *)sbrk(0); n = n - sizeof (*op) - ((int)op & (n - 1)); if (n < 0) n += pagesz; if (n) { if (sbrk(n) == (char *)-1) { unblocksig(); return (NULL); } } bucket = 0; amt = 8; while (pagesz > amt) { amt <<= 1; bucket++; } pagebucket = bucket; } /* * Convert amount of memory requested into (void) closest block size * stored in hash buckets which satisfies request. * Account for space used per block for accounting. */ if (nbytes <= (n = pagesz - sizeof (*op) - RSLOP)) { #ifndef RCHECK amt = 8; /* size of first bucket */ bucket = 0; #else amt = 16; /* size of first bucket */ bucket = 1; #endif n = -(sizeof (*op) + RSLOP); } else { amt = pagesz; bucket = pagebucket; } while (nbytes > amt + n) { amt <<= 1; bucket++; } /* * If nothing in hash bucket right now, * request more memory from the system. */ if ((op = nextf[bucket]) == NULL) { morecore(bucket); if ((op = nextf[bucket]) == NULL) return (NULL); } /* remove from linked list */ nextf[bucket] = op->ov_next; op->ov_magic = MAGIC; op->ov_index = bucket; #ifdef MSTATS nmalloc[bucket]++; #endif #ifdef RCHECK /* * Record allocated size of block and * bound space with magic numbers. */ op->ov_size = (nbytes + RSLOP - 1) & ~(RSLOP - 1); op->ov_rmagic = RMAGIC; *(u_short *)((caddr_t)(op + 1) + op->ov_size) = RMAGIC; #endif unblocksig(); return ((char *)(op + 1)); } /* * Allocate more memory to the indicated bucket. */ static morecore(bucket) int bucket; { register union overhead *op; register int sz; /* size of desired block */ register int amt; /* amount to allocate */ register int nblks; /* how many blocks we get */ sz = 1 << (bucket + 3); if (sz < pagesz) { amt = pagesz; nblks = amt / sz; } else { amt = sz + pagesz; nblks = 1; } op = (union overhead *)sbrk(amt); /* no more room! */ if ((int)op == -1) return; /* * Add new memory allocated to that on * free list for this hash bucket. */ nextf[bucket] = op; while (--nblks > 0) { op->ov_next = (union overhead *)((caddr_t)op + sz); op = (union overhead *)((caddr_t)op + sz); } } free(cp) char *cp; { register int size; register union overhead *op; if (cp == NULL) return; blocksig(); op = (union overhead *)((caddr_t)cp - sizeof (union overhead)); #ifdef DEBUG ASSERT(op->ov_magic == MAGIC); /* make sure it was in use */ #else if (op->ov_magic != MAGIC) { unblocksig(); return; /* sanity */ } #endif #ifdef RCHECK ASSERT(op->ov_rmagic == RMAGIC); ASSERT(*(u_short *)((caddr_t)(op + 1) + op->ov_size) == RMAGIC); #endif size = op->ov_index; ASSERT(size < NBUCKETS); op->ov_next = nextf[size]; nextf[size] = op; #ifdef MSTATS nmalloc[size]--; #endif unblocksig(); } /* * When a program attempts "storage compaction" as mentioned in the * old malloc man page, it realloc's an already freed block. Usually * this is the last block it freed; occasionally it might be farther * back. We have to search all the free lists for the block in order * to determine its bucket: 1st we make one pass thru the lists * checking only the first block in each; if that fails we search * ``realloc_srchlen'' blocks in each list for a match (the variable * is extern so the caller can modify it). If that fails we just copy * however many bytes was given to realloc() and hope it's not huge. */ int realloc_srchlen = 4; /* 4 should be plenty, -1 =>'s whole list */ char * realloc (cp, nbytes) char *cp; unsigned nbytes; { register u_int onb, i; union overhead *op; char *res; int was_alloced = 0; if (cp == NULL) return (malloc(nbytes)); blocksig(); op = (union overhead *)((caddr_t)cp - sizeof (union overhead)); if (op->ov_magic == MAGIC) { was_alloced++; i = op->ov_index; } else { /* * Already free, doing "compaction". * * Search for the old block of memory on the * free list. First, check the most common * case (last element free'd), then (this failing) * the last ``realloc_srchlen'' items free'd. * If all lookups fail, then assume the size of * the memory block being realloc'd is the * smallest possible. */ if ((i = findbucket(op, 1)) < 0 && (i = findbucket(op, realloc_srchlen)) < 0) #ifndef RCHECK i = 0; #else i = 1; /* smallest possible w/ RCHECK */ #endif } onb = 1 << (i + 3); if (onb < pagesz) onb -= sizeof (*op) + RSLOP; else onb += pagesz - sizeof (*op) - RSLOP; /* avoid the copy if same size block */ if (was_alloced) { if (i) { i = 1 << (i + 2); if (i < pagesz) i -= sizeof (*op) + RSLOP; else i += pagesz - sizeof (*op) - RSLOP; } if (nbytes <= onb && nbytes > i) { #ifdef RCHECK op->ov_size = (nbytes + RSLOP - 1) & ~(RSLOP - 1); *(u_short *)((caddr_t)(op + 1) + op->ov_size) = RMAGIC; #endif unblocksig(); return(cp); } else free(cp); } if ((res = malloc(nbytes)) == NULL) { unblocksig(); return (NULL); } if (cp != res) /* common optimization */ Bcopy(cp, res, (nbytes < onb) ? nbytes : onb); unblocksig(); return (res); } /* * Search ``srchlen'' elements of each free list for a block whose * header starts at ``freep''. If srchlen is -1 search the whole list. * Return bucket number, or -1 if not found. */ static findbucket(freep, srchlen) union overhead *freep; int srchlen; { register union overhead *p; register int i, j; for (i = 0; i < NBUCKETS; i++) { j = 0; for (p = nextf[i]; p && j != srchlen; p = p->ov_next) { if (p == freep) return (i); j++; } } return (-1); } #ifdef MSTATS /* * mstats - print out statistics about malloc * * Prints two lines of numbers, one showing the length of the free list * for each size category, the second showing the number of mallocs - * frees for each size category. */ mstats(s) char *s; { register int i, j; register union overhead *p; int totfree = 0, totused = 0; fprintf(stderr, "Memory allocation statistics %s\nfree:\t", s); for (i = 0; i < NBUCKETS; i++) { for (j = 0, p = nextf[i]; p; p = p->ov_next, j++) ; fprintf(stderr, " %d", j); totfree += j * (1 << (i + 3)); } fprintf(stderr, "\nused:\t"); for (i = 0; i < NBUCKETS; i++) { fprintf(stderr, " %d", nmalloc[i]); totused += nmalloc[i] * (1 << (i + 3)); } fprintf(stderr, "\n\tTotal in use: %d, total free: %d\n", totused, totfree); } #endif /* ** Free (p) caddr_t p; ** free's a block of memory with obnoxious signals blocked. ** this is now useless. */ _Free (p) caddr_t p; { free ((char *) p); } /* ** char * ** Calloc (n, s) int n, s; ** blocks annoying signals, calls calloc casting its own args to ** unsigneds for lint's benefit. If it got the memory, unblocks the ** signals and returns the pointer, else punt()'s with an oom error. */ char * _Calloc (n, s) int n, s; { register char *cp = (char *) calloc ((unsigned) n, (unsigned) s); if (cp == NULL) punt ("Out of memory in Calloc!"); return (cp); } /* ** char * ** Malloc (s) int s; ** blocks annoying signals which imply the possibility of calling ** routines which use malloc (stdio, curses, etc.), calls malloc using ** the size s given it. If it gets a non-null pointer, unblocks the ** signals and returns the pointer, else calls punt() with an out-of-mem ** error. */ char * _Malloc (s) int s; { register char *cp = (char *) malloc ((unsigned) s); if (cp == NULL) punt ("Out of memory in Malloc!"); return (cp); } char * newstr (str) register char *str; { return (strcpy (Malloc (strlen(str)+1), str)); } /* the mask of blocked signals from before a call to Calloc, Malloc or Free */ int mask = 0; int blocked = 0; /* malloc occasionally calls itself, I think */ /* this keeps the signals from being blocked */ /* permanently */ blocksig() { if (!blocked) mask = sigblock(sigmask(SIGALRM)|sigmask(SIGQUIT)|sigmask(SIGTSTP)); blocked = 1; } unblocksig() { (void) sigsetmask (mask); blocked = 0; } /* * abort is used to print out info before the program goes down * can be very useful at times. */ abort() { #ifdef MSTATS mstats("at botch time:"); #endif punt("malloc botch"); /* leaves my terminal sane */ }