1 // Adapted for geekos: http://www.cs.umd.edu/~daveho/geekos/
2 // Original version of BGET downloaded from: http://www.fourmilab.ch/bget/
5 // GeekOS changes are (mostly) confined to #if defined (GEEKOS)
14 Designed and implemented in April of 1972 by John Walker, based on the
15 Case Algol OPRO$ algorithm implemented in 1966.
17 Reimplemented in 1975 by John Walker for the Interdata 70.
18 Reimplemented in 1977 by John Walker for the Marinchip 9900.
19 Reimplemented in 1982 by Duff Kurland for the Intel 8080.
21 Portable C version implemented in September of 1990 by an older, wiser
22 instance of the original implementor.
24 Souped up and/or weighed down slightly shortly thereafter by Greg
27 AMIX edition, including the new compaction call-back option, prepared
28 by John Walker in July of 1992.
30 Bug in built-in test program fixed, ANSI compiler warnings eradicated,
31 buffer pool validator implemented, and guaranteed repeatable test
32 added by John Walker in October of 1995.
34 This program is in the public domain.
36 1. This is the book of the generations of Adam. In the day that God
37 created man, in the likeness of God made he him;
38 2. Male and female created he them; and blessed them, and called
39 their name Adam, in the day when they were created.
40 3. And Adam lived an hundred and thirty years, and begat a son in
41 his own likeness, and after his image; and called his name Seth:
42 4. And the days of Adam after he had begotten Seth were eight
43 hundred years: and he begat sons and daughters:
44 5. And all the days that Adam lived were nine hundred and thirty
46 6. And Seth lived an hundred and five years, and begat Enos:
47 7. And Seth lived after he begat Enos eight hundred and seven years,
48 and begat sons and daughters:
49 8. And all the days of Seth were nine hundred and twelve years: and
51 9. And Enos lived ninety years, and begat Cainan:
52 10. And Enos lived after he begat Cainan eight hundred and fifteen
53 years, and begat sons and daughters:
54 11. And all the days of Enos were nine hundred and five years: and
56 12. And Cainan lived seventy years and begat Mahalaleel:
57 13. And Cainan lived after he begat Mahalaleel eight hundred and
58 forty years, and begat sons and daughters:
59 14. And all the days of Cainan were nine hundred and ten years: and
61 15. And Mahalaleel lived sixty and five years, and begat Jared:
62 16. And Mahalaleel lived after he begat Jared eight hundred and
63 thirty years, and begat sons and daughters:
64 17. And all the days of Mahalaleel were eight hundred ninety and
65 five years: and he died.
66 18. And Jared lived an hundred sixty and two years, and he begat
68 19. And Jared lived after he begat Enoch eight hundred years, and
69 begat sons and daughters:
70 20. And all the days of Jared were nine hundred sixty and two years:
72 21. And Enoch lived sixty and five years, and begat Methuselah:
73 22. And Enoch walked with God after he begat Methuselah three
74 hundred years, and begat sons and daughters:
75 23. And all the days of Enoch were three hundred sixty and five
77 24. And Enoch walked with God: and he was not; for God took him.
78 25. And Methuselah lived an hundred eighty and seven years, and
80 26. And Methuselah lived after he begat Lamech seven hundred eighty
81 and two years, and begat sons and daughters:
82 27. And all the days of Methuselah were nine hundred sixty and nine
84 28. And Lamech lived an hundred eighty and two years, and begat a
86 29. And he called his name Noah, saying, This same shall comfort us
87 concerning our work and toil of our hands, because of the ground
88 which the LORD hath cursed.
89 30. And Lamech lived after he begat Noah five hundred ninety and
90 five years, and begat sons and daughters:
91 31. And all the days of Lamech were seven hundred seventy and seven
93 32. And Noah was five hundred years old: and Noah begat Shem, Ham,
96 And buffers begat buffers, and links begat links, and buffer pools
97 begat links to chains of buffer pools containing buffers, and lo the
98 buffers and links and pools of buffers and pools of links to chains of
99 pools of buffers were fruitful and they multiplied and the Operating
100 System looked down upon them and said that it was Good.
106 BGET is a comprehensive memory allocation package which is easily
107 configured to the needs of an application. BGET is efficient in
108 both the time needed to allocate and release buffers and in the
109 memory overhead required for buffer pool management. It
110 automatically consolidates contiguous space to minimise
111 fragmentation. BGET is configured by compile-time definitions,
112 Major options include:
114 * A built-in test program to exercise BGET and
115 demonstrate how the various functions are used.
117 * Allocation by either the "first fit" or "best fit"
120 * Wiping buffers at release time to catch code which
121 references previously released storage.
123 * Built-in routines to dump individual buffers or the
126 * Retrieval of allocation and pool size statistics.
128 * Quantisation of buffer sizes to a power of two to
129 satisfy hardware alignment constraints.
131 * Automatic pool compaction, growth, and shrinkage by
132 means of call-backs to user defined functions.
134 Applications of BGET can range from storage management in
135 ROM-based embedded programs to providing the framework upon which
136 a multitasking system incorporating garbage collection is
137 constructed. BGET incorporates extensive internal consistency
138 checking using the <assert.h> mechanism; all these checks can be
139 turned off by compiling with NDEBUG defined, yielding a version of
140 BGET with minimal size and maximum speed.
142 The basic algorithm underlying BGET has withstood the test of
143 time; more than 25 years have passed since the first
144 implementation of this code. And yet, it is substantially more
145 efficient than the native allocation schemes of many operating
146 systems: the Macintosh and Microsoft Windows to name two, on which
147 programs have obtained substantial speed-ups by layering BGET as
148 an application level memory manager atop the underlying system's.
150 BGET has been implemented on the largest mainframes and the lowest
151 of microprocessors. It has served as the core for multitasking
152 operating systems, multi-thread applications, embedded software in
153 data network switching processors, and a host of C programs. And
154 while it has accreted flexibility and additional options over the
155 years, it remains fast, memory efficient, portable, and easy to
156 integrate into your program.
159 BGET IMPLEMENTATION ASSUMPTIONS
160 ===============================
162 BGET is written in as portable a dialect of C as possible. The
163 only fundamental assumption about the underlying hardware
164 architecture is that memory is allocated is a linear array which
165 can be addressed as a vector of C "char" objects. On segmented
166 address space architectures, this generally means that BGET should
167 be used to allocate storage within a single segment (although some
168 compilers simulate linear address spaces on segmented
169 architectures). On segmented architectures, then, BGET buffer
170 pools may not be larger than a segment, but since BGET allows any
171 number of separate buffer pools, there is no limit on the total
172 storage which can be managed, only on the largest individual
173 object which can be allocated. Machines with a linear address
174 architecture, such as the VAX, 680x0, Sparc, MIPS, or the Intel
175 80386 and above in native mode, may use BGET without restriction.
178 GETTING STARTED WITH BGET
179 =========================
181 Although BGET can be configured in a multitude of fashions, there
182 are three basic ways of working with BGET. The functions
183 mentioned below are documented in the following section. Please
184 excuse the forward references which are made in the interest of
185 providing a roadmap to guide you to the BGET functions you're
188 Embedded Applications
189 ---------------------
191 Embedded applications typically have a fixed area of memory
192 dedicated to buffer allocation (often in a separate RAM address
193 space distinct from the ROM that contains the executable code).
194 To use BGET in such an environment, simply call bpool() with the
195 start address and length of the buffer pool area in RAM, then
196 allocate buffers with bget() and release them with brel().
197 Embedded applications with very limited RAM but abundant CPU speed
198 may benefit by configuring BGET for BestFit allocation (which is
199 usually not worth it in other environments).
204 If the C library malloc() function is too slow, not present in
205 your development environment (for example, an a native Windows or
206 Macintosh program), or otherwise unsuitable, you can replace it
207 with BGET. Initially define a buffer pool of an appropriate size
208 with bpool()--usually obtained by making a call to the operating
209 system's low-level memory allocator. Then allocate buffers with
210 bget(), bgetz(), and bgetr() (the last two permit the allocation
211 of buffers initialised to zero and [inefficient] re-allocation of
212 existing buffers for compatibility with C library functions).
213 Release buffers by calling brel(). If a buffer allocation request
214 fails, obtain more storage from the underlying operating system,
215 add it to the buffer pool by another call to bpool(), and continue
218 Automatic Storage Management
219 ----------------------------
221 You can use BGET as your application's native memory manager and
222 implement automatic storage pool expansion, contraction, and
223 optionally application-specific memory compaction by compiling
224 BGET with the BECtl variable defined, then calling bectl() and
225 supplying functions for storage compaction, acquisition, and
226 release, as well as a standard pool expansion increment. All of
227 these functions are optional (although it doesn't make much sense
228 to provide a release function without an acquisition function,
229 does it?). Once the call-back functions have been defined with
230 bectl(), you simply use bget() and brel() to allocate and release
231 storage as before. You can supply an initial buffer pool with
232 bpool() or rely on automatic allocation to acquire the entire
233 pool. When a call on bget() cannot be satisfied, BGET first
234 checks if a compaction function has been supplied. If so, it is
235 called (with the space required to satisfy the allocation request
236 and a sequence number to allow the compaction routine to be called
237 successively without looping). If the compaction function is able
238 to free any storage (it needn't know whether the storage it freed
239 was adequate) it should return a nonzero value, whereupon BGET
240 will retry the allocation request and, if it fails again, call the
241 compaction function again with the next-higher sequence number.
243 If the compaction function returns zero, indicating failure to
244 free space, or no compaction function is defined, BGET next tests
245 whether a non-NULL allocation function was supplied to bectl().
246 If so, that function is called with an argument indicating how
247 many bytes of additional space are required. This will be the
248 standard pool expansion increment supplied in the call to bectl()
249 unless the original bget() call requested a buffer larger than
250 this; buffers larger than the standard pool block can be managed
251 "off the books" by BGET in this mode. If the allocation function
252 succeeds in obtaining the storage, it returns a pointer to the new
253 block and BGET expands the buffer pool; if it fails, the
254 allocation request fails and returns NULL to the caller. If a
255 non-NULL release function is supplied, expansion blocks which
256 become totally empty are released to the global free pool by
257 passing their addresses to the release function.
259 Equipped with appropriate allocation, release, and compaction
260 functions, BGET can be used as part of very sophisticated memory
261 management strategies, including garbage collection. (Note,
262 however, that BGET is *not* a garbage collector by itself, and
263 that developing such a system requires much additional logic and
264 careful design of the application's memory allocation strategy.)
267 BGET FUNCTION DESCRIPTIONS
268 ==========================
270 Functions implemented in this file (some are enabled by certain of
271 the optional settings below):
273 void bpool(void *buffer, bufsize len);
275 Create a buffer pool of <len> bytes, using the storage starting at
276 <buffer>. You can call bpool() subsequently to contribute
277 additional storage to the overall buffer pool.
279 void *bget(bufsize size);
281 Allocate a buffer of <size> bytes. The address of the buffer is
282 returned, or NULL if insufficient memory was available to allocate
285 void *bgetz(bufsize size);
287 Allocate a buffer of <size> bytes and clear it to all zeroes. The
288 address of the buffer is returned, or NULL if insufficient memory
289 was available to allocate the buffer.
291 void *bgetr(void *buffer, bufsize newsize);
293 Reallocate a buffer previously allocated by bget(), changing its
294 size to <newsize> and preserving all existing data. NULL is
295 returned if insufficient memory is available to reallocate the
296 buffer, in which case the original buffer remains intact.
298 void brel(void *buf);
300 Return the buffer <buf>, previously allocated by bget(), to the
303 void bectl(int (*compact)(bufsize sizereq, int sequence),
304 void *(*acquire)(bufsize size),
305 void (*release)(void *buf),
308 Expansion control: specify functions through which the package may
309 compact storage (or take other appropriate action) when an
310 allocation request fails, and optionally automatically acquire
311 storage for expansion blocks when necessary, and release such
312 blocks when they become empty. If <compact> is non-NULL, whenever
313 a buffer allocation request fails, the <compact> function will be
314 called with arguments specifying the number of bytes (total buffer
315 size, including header overhead) required to satisfy the
316 allocation request, and a sequence number indicating the number of
317 consecutive calls on <compact> attempting to satisfy this
318 allocation request. The sequence number is 1 for the first call
319 on <compact> for a given allocation request, and increments on
320 subsequent calls, permitting the <compact> function to take
321 increasingly dire measures in an attempt to free up storage. If
322 the <compact> function returns a nonzero value, the allocation
323 attempt is re-tried. If <compact> returns 0 (as it must if it
324 isn't able to release any space or add storage to the buffer
325 pool), the allocation request fails, which can trigger automatic
326 pool expansion if the <acquire> argument is non-NULL. At the time
327 the <compact> function is called, the state of the buffer
328 allocator is identical to that at the moment the allocation
329 request was made; consequently, the <compact> function may call
330 brel(), bpool(), bstats(), and/or directly manipulate the buffer
331 pool in any manner which would be valid were the application in
332 control. This does not, however, relieve the <compact> function
333 of the need to ensure that whatever actions it takes do not change
334 things underneath the application that made the allocation
335 request. For example, a <compact> function that released a buffer
336 in the process of being reallocated with bgetr() would lead to
337 disaster. Implementing a safe and effective <compact> mechanism
338 requires careful design of an application's memory architecture,
339 and cannot generally be easily retrofitted into existing code.
341 If <acquire> is non-NULL, that function will be called whenever an
342 allocation request fails. If the <acquire> function succeeds in
343 allocating the requested space and returns a pointer to the new
344 area, allocation will proceed using the expanded buffer pool. If
345 <acquire> cannot obtain the requested space, it should return NULL
346 and the entire allocation process will fail. <pool_incr>
347 specifies the normal expansion block size. Providing an <acquire>
348 function will cause subsequent bget() requests for buffers too
349 large to be managed in the linked-block scheme (in other words,
350 larger than <pool_incr> minus the buffer overhead) to be satisfied
351 directly by calls to the <acquire> function. Automatic release of
352 empty pool blocks will occur only if all pool blocks in the system
353 are the size given by <pool_incr>.
355 void bstats(bufsize *curalloc, bufsize *totfree,
356 bufsize *maxfree, long *nget, long *nrel);
358 The amount of space currently allocated is stored into the
359 variable pointed to by <curalloc>. The total free space (sum of
360 all free blocks in the pool) is stored into the variable pointed
361 to by <totfree>, and the size of the largest single block in the
362 free space pool is stored into the variable pointed to by
363 <maxfree>. The variables pointed to by <nget> and <nrel> are
364 filled, respectively, with the number of successful (non-NULL
365 return) bget() calls and the number of brel() calls.
367 void bstatse(bufsize *pool_incr, long *npool,
368 long *npget, long *nprel,
369 long *ndget, long *ndrel);
371 Extended statistics: The expansion block size will be stored into
372 the variable pointed to by <pool_incr>, or the negative thereof if
373 automatic expansion block releases are disabled. The number of
374 currently active pool blocks will be stored into the variable
375 pointed to by <npool>. The variables pointed to by <npget> and
376 <nprel> will be filled with, respectively, the number of expansion
377 block acquisitions and releases which have occurred. The
378 variables pointed to by <ndget> and <ndrel> will be filled with
379 the number of bget() and brel() calls, respectively, managed
380 through blocks directly allocated by the acquisition and release
383 void bufdump(void *buf);
385 The buffer pointed to by <buf> is dumped on standard output.
387 void bpoold(void *pool, int dumpalloc, int dumpfree);
389 All buffers in the buffer pool <pool>, previously initialised by a
390 call on bpool(), are listed in ascending memory address order. If
391 <dumpalloc> is nonzero, the contents of allocated buffers are
392 dumped; if <dumpfree> is nonzero, the contents of free blocks are
395 int bpoolv(void *pool);
397 The named buffer pool, previously initialised by a call on
398 bpool(), is validated for bad pointers, overwritten data, etc. If
399 compiled with NDEBUG not defined, any error generates an assertion
400 failure. Otherwise 1 is returned if the pool is valid, 0 if an
408 /*#define TestProg 20000*/ /* Generate built-in test program
409 if defined. The value specifies
410 how many buffer allocation attempts
411 the test program should make. */
413 #define SizeQuant 4 /* Buffer allocation size quantum:
414 all buffers allocated are a
415 multiple of this size. This
416 MUST be a power of two. */
418 /*#define BufDump 1*/ /* Define this symbol to enable the
419 bpoold() function which dumps the
420 buffers in a buffer pool. */
422 /*#define BufValid 1*/ /* Define this symbol to enable the
423 bpoolv() function for validating
426 /*#define DumpData 1*/ /* Define this symbol to enable the
427 bufdump() function which allows
428 dumping the contents of an allocated
431 /*#define BufStats 1*/ /* Define this symbol to enable the
432 bstats() function which calculates
433 the total free space in the buffer
434 pool, the largest available
435 buffer, and the total space
436 currently allocated. */
438 /*#define FreeWipe 1*/ /* Wipe free buffers to a guaranteed
439 pattern of garbage to trip up
440 miscreants who attempt to use
441 pointers into released buffers. */
443 #define BestFit 1 /* Use a best fit algorithm when
444 searching for space for an
445 allocation request. This uses
446 memory more efficiently, but
447 allocation will be much slower. */
449 /*#define BECtl 1*/ /* Define this symbol to enable the
450 bectl() function for automatic
451 pool space control. */
455 #include <geekos/string.h> // for memset()
457 // Provide an assert() macro
458 #include <geekos/kassert.h>
459 #define assert(exp) KASSERT(exp)
461 #else // define (GEEKOS)
466 #define NDEBUG /* Exits in asserts confuse lint */
467 /* LINTLIBRARY */ /* Don't complain about def, no ref */
468 extern char *sprintf(); /* Sun includes don't define sprintf */
474 #endif // defined (GEEKOS)
476 #ifdef BufDump /* BufDump implies DumpData */
486 /* Declare the interface, including the requested buffer size type,
489 #include <geekos/bget.h>
491 #define MemSize int /* Type for size arguments to memxxx()
492 functions such as memcmp(). */
497 struct bfhead *flink; /* Forward link */
498 struct bfhead *blink; /* Backward link */
501 /* Header in allocated and free buffers */
504 bufsize prevfree; /* Relative link back to previous
505 free buffer in memory or 0 if
506 previous buffer is allocated. */
507 bufsize bsize; /* Buffer size: positive if free,
508 negative if allocated. */
510 #define BH(p) ((struct bhead *) (p))
512 /* Header in directly allocated buffers (by acqfcn) */
515 bufsize tsize; /* Total size, including overhead */
516 struct bhead bh; /* Common header */
518 #define BDH(p) ((struct bdhead *) (p))
520 /* Header in free buffers */
523 struct bhead bh; /* Common allocated/free header */
524 struct qlinks ql; /* Links on free list */
526 #define BFH(p) ((struct bfhead *) (p))
528 static struct bfhead freelist = { /* List of free buffers */
530 {&freelist, &freelist}
535 static bufsize totalloc = 0; /* Total space currently allocated */
536 static long numget = 0, numrel = 0; /* Number of bget() and brel() calls */
538 static long numpblk = 0; /* Number of pool blocks */
539 static long numpget = 0, numprel = 0; /* Number of block gets and rels */
540 static long numdget = 0, numdrel = 0; /* Number of direct gets and rels */
542 #endif /* BufStats */
546 /* Automatic expansion block management functions */
548 static int (*compfcn) _((bufsize sizereq, int sequence)) = NULL;
549 static void *(*acqfcn) _((bufsize size)) = NULL;
550 static void (*relfcn) _((void *buf)) = NULL;
552 static bufsize exp_incr = 0; /* Expansion block size */
553 static bufsize pool_len = 0; /* 0: no bpool calls have been made
554 -1: not all pool blocks are
556 >0: (common) block size for all
557 bpool calls made so far
561 /* Minimum allocation quantum: */
563 #define QLSize (sizeof(struct qlinks))
564 #define SizeQ ((SizeQuant > QLSize) ? SizeQuant : QLSize)
566 #define V (void) /* To denote unwanted returned values */
568 /* End sentinel: value placed in bsize field of dummy block delimiting
569 end of pool block. The most negative number which will fit in a
570 bufsize, defined in a way that the compiler will accept. */
572 #define ESent ((bufsize) (-(((1L << (sizeof(bufsize) * 8 - 2)) - 1) * 2) - 2))
574 /* BGET -- Allocate a buffer. */
576 void *bget(requested_size)
577 bufsize requested_size;
579 bufsize size = requested_size;
591 if (size < SizeQ) { /* Need at least room for the */
592 size = SizeQ; /* queue links. */
596 size = (size + (SizeQuant - 1)) & (~(SizeQuant - 1));
600 size += sizeof(struct bhead); /* Add overhead in allocated buffer
604 /* If a compact function was provided in the call to bectl(), wrap
605 a loop around the allocation process to allow compaction to
606 intervene in case we don't find a suitable buffer in the chain. */
610 b = freelist.ql.flink;
616 /* Scan the free list searching for the first buffer big enough
617 to hold the requested size buffer. */
620 while (b != &freelist) {
621 if (b->bh.bsize >= size) {
622 if ((best == &freelist) || (b->bh.bsize < best->bh.bsize)) {
626 b = b->ql.flink; /* Link to next buffer */
631 while (b != &freelist) {
632 if ((bufsize) b->bh.bsize >= size) {
634 /* Buffer is big enough to satisfy the request. Allocate it
635 to the caller. We must decide whether the buffer is large
636 enough to split into the part given to the caller and a
637 free buffer that remains on the free list, or whether the
638 entire buffer should be removed from the free list and
639 given to the caller in its entirety. We only split the
640 buffer if enough room remains for a header plus the minimum
641 quantum of allocation. */
643 if ((b->bh.bsize - size) > (SizeQ + (sizeof(struct bhead)))) {
644 struct bhead *ba, *bn;
646 ba = BH(((char *) b) + (b->bh.bsize - size));
647 bn = BH(((char *) ba) + size);
648 assert(bn->prevfree == b->bh.bsize);
649 /* Subtract size from length of free block. */
651 /* Link allocated buffer to the previous free buffer. */
652 ba->prevfree = b->bh.bsize;
653 /* Plug negative size into user buffer. */
654 ba->bsize = -(bufsize) size;
655 /* Mark buffer after this one not preceded by free block. */
660 numget++; /* Increment number of bget() calls */
662 buf = (void *) ((((char *) ba) + sizeof(struct bhead)));
667 ba = BH(((char *) b) + b->bh.bsize);
668 assert(ba->prevfree == b->bh.bsize);
670 /* The buffer isn't big enough to split. Give the whole
671 shebang to the caller and remove it from the free list. */
673 assert(b->ql.blink->ql.flink == b);
674 assert(b->ql.flink->ql.blink == b);
675 b->ql.blink->ql.flink = b->ql.flink;
676 b->ql.flink->ql.blink = b->ql.blink;
679 totalloc += b->bh.bsize;
680 numget++; /* Increment number of bget() calls */
682 /* Negate size to mark buffer allocated. */
683 b->bh.bsize = -(b->bh.bsize);
685 /* Zero the back pointer in the next buffer in memory
686 to indicate that this buffer is allocated. */
689 /* Give user buffer starting at queue links. */
690 buf = (void *) &(b->ql);
694 b = b->ql.flink; /* Link to next buffer */
698 /* We failed to find a buffer. If there's a compact function
699 defined, notify it of the size requested. If it returns
700 true, try the allocation again. */
702 if ((compfcn == NULL) || (!(*compfcn)(size, ++compactseq))) {
707 /* No buffer available with requested size free. */
709 /* Don't give up yet -- look in the reserve supply. */
711 if (acqfcn != NULL) {
712 if (size > exp_incr - sizeof(struct bhead)) {
714 /* Request is too large to fit in a single expansion
715 block. Try to satisy it by a direct buffer acquisition. */
719 size += sizeof(struct bdhead) - sizeof(struct bhead);
720 if ((bdh = BDH((*acqfcn)((bufsize) size))) != NULL) {
722 /* Mark the buffer special by setting the size field
723 of its header to zero. */
725 bdh->bh.prevfree = 0;
729 numget++; /* Increment number of bget() calls */
730 numdget++; /* Direct bget() call count */
732 buf = (void *) (bdh + 1);
738 /* Try to obtain a new expansion block */
742 if ((newpool = (*acqfcn)((bufsize) exp_incr)) != NULL) {
743 bpool(newpool, exp_incr);
744 buf = bget(requested_size); /* This can't, I say, can't
751 /* Still no buffer available */
758 /* BGETZ -- Allocate a buffer and clear its contents to zero. We clear
759 the entire contents of the buffer to zero, not just the
760 region requested by the caller. */
765 char *buf = (char *) bget(size);
771 b = BH(buf - sizeof(struct bhead));
776 bd = BDH(buf - sizeof(struct bdhead));
777 rsize = bd->tsize - sizeof(struct bdhead);
779 rsize -= sizeof(struct bhead);
781 assert(rsize >= size);
782 V memset(buf, 0, (MemSize) rsize);
784 return ((void *) buf);
787 /* BGETR -- Reallocate a buffer. This is a minimal implementation,
788 simply in terms of brel() and bget(). It could be
789 enhanced to allow the buffer to grow into adjacent free
790 blocks and to avoid moving data unnecessarily. */
792 void *bgetr(buf, size)
797 bufsize osize; /* Old size of buffer */
800 if ((nbuf = bget(size)) == NULL) { /* Acquire new buffer */
806 b = BH(((char *) buf) - sizeof(struct bhead));
810 /* Buffer acquired directly through acqfcn. */
813 bd = BDH(((char *) buf) - sizeof(struct bdhead));
814 osize = bd->tsize - sizeof(struct bdhead);
817 osize -= sizeof(struct bhead);
819 V memcpy((char *) nbuf, (char *) buf, /* Copy the data */
820 (MemSize) ((size < osize) ? size : osize));
825 /* BREL -- Release a buffer. */
830 struct bfhead *b, *bn;
832 b = BFH(((char *) buf) - sizeof(struct bhead));
834 numrel++; /* Increment number of brel() calls */
839 if (b->bh.bsize == 0) { /* Directly-acquired buffer? */
842 bdh = BDH(((char *) buf) - sizeof(struct bdhead));
843 assert(b->bh.prevfree == 0);
845 totalloc -= bdh->tsize;
846 assert(totalloc >= 0);
847 numdrel++; /* Number of direct releases */
848 #endif /* BufStats */
850 V memset((char *) buf, 0x55,
851 (MemSize) (bdh->tsize - sizeof(struct bdhead)));
852 #endif /* FreeWipe */
853 assert(relfcn != NULL);
854 (*relfcn)((void *) bdh); /* Release it directly. */
859 /* Buffer size must be negative, indicating that the buffer is
862 if (b->bh.bsize >= 0) {
865 assert(b->bh.bsize < 0);
867 /* Back pointer in next buffer must be zero, indicating the
870 assert(BH((char *) b - b->bh.bsize)->prevfree == 0);
873 totalloc += b->bh.bsize;
874 assert(totalloc >= 0);
877 /* If the back link is nonzero, the previous buffer is free. */
879 if (b->bh.prevfree != 0) {
881 /* The previous buffer is free. Consolidate this buffer with it
882 by adding the length of this buffer to the previous free
883 buffer. Note that we subtract the size in the buffer being
884 released, since it's negative to indicate that the buffer is
887 register bufsize size = b->bh.bsize;
889 /* Make the previous buffer the one we're working on. */
890 assert(BH((char *) b - b->bh.prevfree)->bsize == b->bh.prevfree);
891 b = BFH(((char *) b) - b->bh.prevfree);
895 /* The previous buffer isn't allocated. Insert this buffer
896 on the free list as an isolated free block. */
898 assert(freelist.ql.blink->ql.flink == &freelist);
899 assert(freelist.ql.flink->ql.blink == &freelist);
900 b->ql.flink = &freelist;
901 b->ql.blink = freelist.ql.blink;
902 freelist.ql.blink = b;
903 b->ql.blink->ql.flink = b;
904 b->bh.bsize = -b->bh.bsize;
907 /* Now we look at the next buffer in memory, located by advancing from
908 the start of this buffer by its size, to see if that buffer is
909 free. If it is, we combine this buffer with the next one in
910 memory, dechaining the second buffer from the free list. */
912 bn = BFH(((char *) b) + b->bh.bsize);
913 if (bn->bh.bsize > 0) {
915 /* The buffer is free. Remove it from the free list and add
916 its size to that of our buffer. */
918 assert(BH((char *) bn + bn->bh.bsize)->prevfree == bn->bh.bsize);
919 assert(bn->ql.blink->ql.flink == bn);
920 assert(bn->ql.flink->ql.blink == bn);
921 bn->ql.blink->ql.flink = bn->ql.flink;
922 bn->ql.flink->ql.blink = bn->ql.blink;
923 b->bh.bsize += bn->bh.bsize;
925 /* Finally, advance to the buffer that follows the newly
926 consolidated free block. We must set its backpointer to the
927 head of the consolidated free block. We know the next block
928 must be an allocated block because the process of recombination
929 guarantees that two free blocks will never be contiguous in
932 bn = BFH(((char *) b) + b->bh.bsize);
935 V memset(((char *) b) + sizeof(struct bfhead), 0x55,
936 (MemSize) (b->bh.bsize - sizeof(struct bfhead)));
938 assert(bn->bh.bsize < 0);
940 /* The next buffer is allocated. Set the backpointer in it to point
941 to this buffer; the previous free buffer in memory. */
943 bn->bh.prevfree = b->bh.bsize;
947 /* If a block-release function is defined, and this free buffer
948 constitutes the entire block, release it. Note that pool_len
949 is defined in such a way that the test will fail unless all
950 pool blocks are the same size. */
952 if (relfcn != NULL &&
953 ((bufsize) b->bh.bsize) == (pool_len - sizeof(struct bhead))) {
955 assert(b->bh.prevfree == 0);
956 assert(BH((char *) b + b->bh.bsize)->bsize == ESent);
957 assert(BH((char *) b + b->bh.bsize)->prevfree == b->bh.bsize);
958 /* Unlink the buffer from the free list */
959 b->ql.blink->ql.flink = b->ql.flink;
960 b->ql.flink->ql.blink = b->ql.blink;
964 numprel++; /* Nr of expansion block releases */
965 numpblk--; /* Total number of blocks */
966 assert(numpblk == numpget - numprel);
967 #endif /* BufStats */
974 /* BECTL -- Establish automatic pool expansion control */
976 void bectl(compact, acquire, release, pool_incr)
977 int (*compact) _((bufsize sizereq, int sequence));
978 void *(*acquire) _((bufsize size));
979 void (*release) _((void *buf));
985 exp_incr = pool_incr;
989 /* BPOOL -- Add a region of memory to the buffer pool. */
995 struct bfhead *b = BFH(buf);
999 len &= ~(SizeQuant - 1);
1002 if (pool_len == 0) {
1004 } else if (len != pool_len) {
1008 numpget++; /* Number of block acquisitions */
1009 numpblk++; /* Number of blocks total */
1010 assert(numpblk == numpget - numprel);
1011 #endif /* BufStats */
1014 /* Since the block is initially occupied by a single free buffer,
1015 it had better not be (much) larger than the largest buffer
1016 whose size we can store in bhead.bsize. */
1018 assert(len - sizeof(struct bhead) <= -((bufsize) ESent + 1));
1020 /* Clear the backpointer at the start of the block to indicate that
1021 there is no free block prior to this one. That blocks
1022 recombination when the first block in memory is released. */
1026 /* Chain the new block to the free list. */
1028 assert(freelist.ql.blink->ql.flink == &freelist);
1029 assert(freelist.ql.flink->ql.blink == &freelist);
1030 b->ql.flink = &freelist;
1031 b->ql.blink = freelist.ql.blink;
1032 freelist.ql.blink = b;
1033 b->ql.blink->ql.flink = b;
1035 /* Create a dummy allocated buffer at the end of the pool. This dummy
1036 buffer is seen when a buffer at the end of the pool is released and
1037 blocks recombination of the last buffer with the dummy buffer at
1038 the end. The length in the dummy buffer is set to the largest
1039 negative number to denote the end of the pool for diagnostic
1040 routines (this specific value is not counted on by the actual
1041 allocation and release functions). */
1043 len -= sizeof(struct bhead);
1044 b->bh.bsize = (bufsize) len;
1046 V memset(((char *) b) + sizeof(struct bfhead), 0x55,
1047 (MemSize) (len - sizeof(struct bfhead)));
1049 bn = BH(((char *) b) + len);
1050 bn->prevfree = (bufsize) len;
1051 /* Definition of ESent assumes two's complement! */
1058 /* BSTATS -- Return buffer allocation free space statistics. */
1060 void bstats(curalloc, totfree, maxfree, nget, nrel)
1061 bufsize *curalloc, *totfree, *maxfree;
1064 struct bfhead *b = freelist.ql.flink;
1068 *curalloc = totalloc;
1071 while (b != &freelist) {
1072 assert(b->bh.bsize > 0);
1073 *totfree += b->bh.bsize;
1074 if (b->bh.bsize > *maxfree) {
1075 *maxfree = b->bh.bsize;
1077 b = b->ql.flink; /* Link to next buffer */
1083 /* BSTATSE -- Return extended statistics */
1085 void bstatse(pool_incr, npool, npget, nprel, ndget, ndrel)
1087 long *npool, *npget, *nprel, *ndget, *ndrel;
1089 *pool_incr = (pool_len < 0) ? -exp_incr : exp_incr;
1097 #endif /* BufStats */
1101 /* BUFDUMP -- Dump the data in a buffer. This is called with the user
1102 data pointer, and backs up to the buffer header. It will
1103 dump either a free block or an allocated one. */
1112 b = BFH(((char *) buf) - sizeof(struct bhead));
1113 assert(b->bh.bsize != 0);
1114 if (b->bh.bsize < 0) {
1115 bdump = (uchar_t *) buf;
1116 bdlen = (-b->bh.bsize) - sizeof(struct bhead);
1118 bdump = (uchar_t *) (((char *) b) + sizeof(struct bfhead));
1119 bdlen = b->bh.bsize - sizeof(struct bfhead);
1125 char bhex[50], bascii[20];
1131 for (i = 0; i < l; i++) {
1132 V sprintf(bhex + i * 3, "%02X ", bdump[i]);
1133 bascii[i] = isprint(bdump[i]) ? bdump[i] : ' ';
1136 V printf("%-48s %s\n", bhex, bascii);
1139 while ((bdlen > 16) && (memcmp((char *) (bdump - 16),
1140 (char *) bdump, 16) == 0)) {
1147 " (%d lines [%d bytes] identical to above line skipped)\n",
1149 } else if (dupes == 1) {
1159 /* BPOOLD -- Dump a buffer pool. The buffer headers are always listed.
1160 If DUMPALLOC is nonzero, the contents of allocated buffers
1161 are dumped. If DUMPFREE is nonzero, free blocks are
1162 dumped as well. If FreeWipe checking is enabled, free
1163 blocks which have been clobbered will always be dumped. */
1165 void bpoold(buf, dumpalloc, dumpfree)
1167 int dumpalloc, dumpfree;
1169 struct bfhead *b = BFH(buf);
1171 while (b->bh.bsize != ESent) {
1172 bufsize bs = b->bh.bsize;
1176 V printf("Allocated buffer: size %6ld bytes.\n", (long) bs);
1178 bufdump((void *) (((char *) b) + sizeof(struct bhead)));
1184 if ((b->ql.blink->ql.flink != b) ||
1185 (b->ql.flink->ql.blink != b)) {
1186 lerr = " (Bad free list links)";
1188 V printf("Free block: size %6ld bytes.%s\n",
1191 lerr = ((char *) b) + sizeof(struct bfhead);
1192 if ((bs > sizeof(struct bfhead)) && ((*lerr != 0x55) ||
1193 (memcmp(lerr, lerr + 1,
1194 (MemSize) (bs - (sizeof(struct bfhead) + 1))) != 0))) {
1196 "(Contents of above free block have been overstored.)\n");
1197 bufdump((void *) (((char *) b) + sizeof(struct bhead)));
1201 bufdump((void *) (((char *) b) + sizeof(struct bhead)));
1204 b = BFH(((char *) b) + bs);
1207 #endif /* BufDump */
1211 /* BPOOLV -- Validate a buffer pool. If NDEBUG isn't defined,
1212 any error generates an assertion failure. */
1217 struct bfhead *b = BFH(buf);
1219 while (b->bh.bsize != ESent) {
1220 bufsize bs = b->bh.bsize;
1231 if ((b->ql.blink->ql.flink != b) ||
1232 (b->ql.flink->ql.blink != b)) {
1233 V printf("Free block: size %6ld bytes. (Bad free list links)\n",
1239 lerr = ((char *) b) + sizeof(struct bfhead);
1240 if ((bs > sizeof(struct bfhead)) && ((*lerr != 0x55) ||
1241 (memcmp(lerr, lerr + 1,
1242 (MemSize) (bs - (sizeof(struct bfhead) + 1))) != 0))) {
1244 "(Contents of above free block have been overstored.)\n");
1245 bufdump((void *) (((char *) b) + sizeof(struct bhead)));
1251 b = BFH(((char *) b) + bs);
1255 #endif /* BufValid */
1257 /***********************\
1259 * Built-in test program *
1261 \***********************/
1265 #define Repeatable 1 /* Repeatable pseudorandom sequence */
1266 /* If Repeatable is not defined, a
1267 time-seeded pseudorandom sequence
1268 is generated, exercising BGET with
1269 a different pattern of calls on each
1271 #define OUR_RAND /* Use our own built-in version of
1272 rand() to guarantee the test is
1276 #define PoolSize 300000 /* Test buffer pool size */
1278 #define PoolSize 50000 /* Test buffer pool size */
1280 #define ExpIncr 32768 /* Test expansion block size */
1281 #define CompactTries 10 /* Maximum tries at compacting */
1283 #define dumpAlloc 0 /* Dump allocated buffers ? */
1284 #define dumpFree 0 /* Dump free buffers ? */
1290 extern char *malloc();
1291 extern int free _((char *));
1293 static char *bchain = NULL; /* Our private buffer chain */
1294 static char *bp = NULL; /* Our initial buffer pool */
1300 static ulong_t int next = 1;
1302 /* Return next random integer */
1306 next = next * 1103515245L + 12345;
1307 return (uint_t) (next / 65536L) % 32768L;
1310 /* Set seed for random generator */
1319 /* STATS -- Edit statistics returned by bstats() or bstatse(). */
1321 static void stats(when)
1324 bufsize cural, totfree, maxfree;
1328 long totblocks, npget, nprel, ndget, ndrel;
1331 bstats(&cural, &totfree, &maxfree, &nget, &nfree);
1333 "%s: %ld gets, %ld releases. %ld in use, %ld free, largest = %ld\n",
1334 when, nget, nfree, (long) cural, (long) totfree, (long) maxfree);
1336 bstatse(&pincr, &totblocks, &npget, &nprel, &ndget, &ndrel);
1338 " Blocks: size = %ld, %ld (%ld bytes) in use, %ld gets, %ld frees\n",
1339 (long)pincr, totblocks, pincr * totblocks, npget, nprel);
1340 V printf(" %ld direct gets, %ld direct frees\n", ndget, ndrel);
1345 static int protect = 0; /* Disable compaction during bgetr() */
1347 /* BCOMPACT -- Compaction call-back function. */
1349 static int bcompact(bsize, seq)
1355 int i = rand() & 0x3;
1358 V printf("Compaction requested. %ld bytes needed, sequence %d.\n",
1362 if (protect || (seq > CompactTries)) {
1364 V printf("Compaction gave up.\n");
1369 /* Based on a random cast, release a random buffer in the list
1370 of allocated buffers. */
1372 while (i > 0 && bc != NULL) {
1373 bc = *((char **) bc);
1379 fb = *((char **) bc);
1381 *((char **) bc) = *((char **) fb);
1388 V printf("Compaction bailed out.\n");
1390 #endif /* CompactTries */
1394 /* BEXPAND -- Expand pool call-back function. */
1396 static void *bexpand(size)
1400 bufsize cural, totfree, maxfree;
1403 /* Don't expand beyond the total allocated size given by PoolSize. */
1405 bstats(&cural, &totfree, &maxfree, &nget, &nfree);
1407 if (cural < PoolSize) {
1408 np = (void *) malloc((unsigned) size);
1411 V printf("Expand pool by %ld -- %s.\n", (long) size,
1412 np == NULL ? "failed" : "succeeded");
1417 /* BSHRINK -- Shrink buffer pool call-back function. */
1419 static void bshrink(buf)
1422 if (((char *) buf) == bp) {
1424 V printf("Initial pool released.\n");
1429 V printf("Shrink pool.\n");
1436 /* Restrict buffer requests to those large enough to contain our pointer and
1437 small enough for the CPU architecture. */
1439 static bufsize blimit(bs)
1442 if (bs < sizeof(char *)) {
1443 bs = sizeof(char *);
1446 /* This is written out in this ugly fashion because the
1447 cool expression in sizeof(int) that auto-configured
1448 to any length int befuddled some compilers. */
1450 if (sizeof(int) == 2) {
1467 /* Seed the random number generator. If Repeatable is defined, we
1468 always use the same seed. Otherwise, we seed from the clock to
1469 shake things up from run to run. */
1474 V srand((int) time((long *) NULL));
1477 /* Compute x such that pow(x, p) ranges between 1 and 4*ExpIncr as
1478 p ranges from 0 to ExpIncr-1, with a concentration in the lower
1483 x = exp(log(4.0 * ExpIncr) / (ExpIncr - 1.0));
1486 bectl(bcompact, bexpand, bshrink, (bufsize) ExpIncr);
1487 bp = malloc(ExpIncr);
1489 bpool((void *) bp, (bufsize) ExpIncr);
1491 bp = malloc(PoolSize);
1493 bpool((void *) bp, (bufsize) PoolSize);
1496 stats("Create pool");
1497 V bpoolv((void *) bp);
1498 bpoold((void *) bp, dumpAlloc, dumpFree);
1500 for (i = 0; i < TestProg; i++) {
1502 bufsize bs = pow(x, (double) (rand() & (ExpIncr - 1)));
1504 assert(bs <= (((bufsize) 4) * ExpIncr));
1506 if (rand() & 0x400) {
1507 cb = (char *) bgetz(bs);
1509 cb = (char *) bget(bs);
1520 fb = *((char **) bc);
1522 *((char **) bc) = *((char **) fb);
1529 *((char **) cb) = (char *) bchain;
1532 /* Based on a random cast, release a random buffer in the list
1533 of allocated buffers. */
1535 if ((rand() & 0x10) == 0) {
1537 int i = rand() & 0x3;
1539 while (i > 0 && bc != NULL) {
1540 bc = *((char **) bc);
1546 fb = *((char **) bc);
1548 *((char **) bc) = *((char **) fb);
1554 /* Based on a random cast, reallocate a random buffer in the list
1557 if ((rand() & 0x20) == 0) {
1559 int i = rand() & 0x3;
1561 while (i > 0 && bc != NULL) {
1562 bc = *((char **) bc);
1568 fb = *((char **) bc);
1572 bs = pow(x, (double) (rand() & (ExpIncr - 1)));
1575 protect = 1; /* Protect against compaction */
1577 newb = (char *) bgetr((void *) fb, bs);
1582 *((char **) bc) = newb;
1588 stats("\nAfter allocation");
1590 V bpoolv((void *) bp);
1591 bpoold((void *) bp, dumpAlloc, dumpFree);
1594 while (bchain != NULL) {
1597 bchain = *((char **) buf);
1600 stats("\nAfter release");
1603 V bpoolv((void *) bp);
1604 bpoold((void *) bp, dumpAlloc, dumpFree);