1*074ba9b2SJens Wiklander /* 2*074ba9b2SJens Wiklander 3*074ba9b2SJens Wiklander B G E T 4*074ba9b2SJens Wiklander 5*074ba9b2SJens Wiklander Buffer allocator 6*074ba9b2SJens Wiklander 7*074ba9b2SJens Wiklander Designed and implemented in April of 1972 by John Walker, based on the 8*074ba9b2SJens Wiklander Case Algol OPRO$ algorithm implemented in 1966. 9*074ba9b2SJens Wiklander 10*074ba9b2SJens Wiklander Reimplemented in 1975 by John Walker for the Interdata 70. 11*074ba9b2SJens Wiklander Reimplemented in 1977 by John Walker for the Marinchip 9900. 12*074ba9b2SJens Wiklander Reimplemented in 1982 by Duff Kurland for the Intel 8080. 13*074ba9b2SJens Wiklander 14*074ba9b2SJens Wiklander Portable C version implemented in September of 1990 by an older, wiser 15*074ba9b2SJens Wiklander instance of the original implementor. 16*074ba9b2SJens Wiklander 17*074ba9b2SJens Wiklander Souped up and/or weighed down slightly shortly thereafter by Greg 18*074ba9b2SJens Wiklander Lutz. 19*074ba9b2SJens Wiklander 20*074ba9b2SJens Wiklander AMIX edition, including the new compaction call-back option, prepared 21*074ba9b2SJens Wiklander by John Walker in July of 1992. 22*074ba9b2SJens Wiklander 23*074ba9b2SJens Wiklander Bug in built-in test program fixed, ANSI compiler warnings eradicated, 24*074ba9b2SJens Wiklander buffer pool validator implemented, and guaranteed repeatable test 25*074ba9b2SJens Wiklander added by John Walker in October of 1995. 26*074ba9b2SJens Wiklander 27*074ba9b2SJens Wiklander This program is in the public domain. 28*074ba9b2SJens Wiklander 29*074ba9b2SJens Wiklander 1. This is the book of the generations of Adam. In the day that God 30*074ba9b2SJens Wiklander created man, in the likeness of God made he him; 31*074ba9b2SJens Wiklander 2. Male and female created he them; and blessed them, and called 32*074ba9b2SJens Wiklander their name Adam, in the day when they were created. 33*074ba9b2SJens Wiklander 3. And Adam lived an hundred and thirty years, and begat a son in 34*074ba9b2SJens Wiklander his own likeness, and after his image; and called his name Seth: 35*074ba9b2SJens Wiklander 4. And the days of Adam after he had begotten Seth were eight 36*074ba9b2SJens Wiklander hundred years: and he begat sons and daughters: 37*074ba9b2SJens Wiklander 5. And all the days that Adam lived were nine hundred and thirty 38*074ba9b2SJens Wiklander years: and he died. 39*074ba9b2SJens Wiklander 6. And Seth lived an hundred and five years, and begat Enos: 40*074ba9b2SJens Wiklander 7. And Seth lived after he begat Enos eight hundred and seven years, 41*074ba9b2SJens Wiklander and begat sons and daughters: 42*074ba9b2SJens Wiklander 8. And all the days of Seth were nine hundred and twelve years: and 43*074ba9b2SJens Wiklander he died. 44*074ba9b2SJens Wiklander 9. And Enos lived ninety years, and begat Cainan: 45*074ba9b2SJens Wiklander 10. And Enos lived after he begat Cainan eight hundred and fifteen 46*074ba9b2SJens Wiklander years, and begat sons and daughters: 47*074ba9b2SJens Wiklander 11. And all the days of Enos were nine hundred and five years: and 48*074ba9b2SJens Wiklander he died. 49*074ba9b2SJens Wiklander 12. And Cainan lived seventy years and begat Mahalaleel: 50*074ba9b2SJens Wiklander 13. And Cainan lived after he begat Mahalaleel eight hundred and 51*074ba9b2SJens Wiklander forty years, and begat sons and daughters: 52*074ba9b2SJens Wiklander 14. And all the days of Cainan were nine hundred and ten years: and 53*074ba9b2SJens Wiklander he died. 54*074ba9b2SJens Wiklander 15. And Mahalaleel lived sixty and five years, and begat Jared: 55*074ba9b2SJens Wiklander 16. And Mahalaleel lived after he begat Jared eight hundred and 56*074ba9b2SJens Wiklander thirty years, and begat sons and daughters: 57*074ba9b2SJens Wiklander 17. And all the days of Mahalaleel were eight hundred ninety and 58*074ba9b2SJens Wiklander five years: and he died. 59*074ba9b2SJens Wiklander 18. And Jared lived an hundred sixty and two years, and he begat 60*074ba9b2SJens Wiklander Enoch: 61*074ba9b2SJens Wiklander 19. And Jared lived after he begat Enoch eight hundred years, and 62*074ba9b2SJens Wiklander begat sons and daughters: 63*074ba9b2SJens Wiklander 20. And all the days of Jared were nine hundred sixty and two years: 64*074ba9b2SJens Wiklander and he died. 65*074ba9b2SJens Wiklander 21. And Enoch lived sixty and five years, and begat Methuselah: 66*074ba9b2SJens Wiklander 22. And Enoch walked with God after he begat Methuselah three 67*074ba9b2SJens Wiklander hundred years, and begat sons and daughters: 68*074ba9b2SJens Wiklander 23. And all the days of Enoch were three hundred sixty and five 69*074ba9b2SJens Wiklander years: 70*074ba9b2SJens Wiklander 24. And Enoch walked with God: and he was not; for God took him. 71*074ba9b2SJens Wiklander 25. And Methuselah lived an hundred eighty and seven years, and 72*074ba9b2SJens Wiklander begat Lamech. 73*074ba9b2SJens Wiklander 26. And Methuselah lived after he begat Lamech seven hundred eighty 74*074ba9b2SJens Wiklander and two years, and begat sons and daughters: 75*074ba9b2SJens Wiklander 27. And all the days of Methuselah were nine hundred sixty and nine 76*074ba9b2SJens Wiklander years: and he died. 77*074ba9b2SJens Wiklander 28. And Lamech lived an hundred eighty and two years, and begat a 78*074ba9b2SJens Wiklander son: 79*074ba9b2SJens Wiklander 29. And he called his name Noah, saying, This same shall comfort us 80*074ba9b2SJens Wiklander concerning our work and toil of our hands, because of the ground 81*074ba9b2SJens Wiklander which the LORD hath cursed. 82*074ba9b2SJens Wiklander 30. And Lamech lived after he begat Noah five hundred ninety and 83*074ba9b2SJens Wiklander five years, and begat sons and daughters: 84*074ba9b2SJens Wiklander 31. And all the days of Lamech were seven hundred seventy and seven 85*074ba9b2SJens Wiklander years: and he died. 86*074ba9b2SJens Wiklander 32. And Noah was five hundred years old: and Noah begat Shem, Ham, 87*074ba9b2SJens Wiklander and Japheth. 88*074ba9b2SJens Wiklander 89*074ba9b2SJens Wiklander And buffers begat buffers, and links begat links, and buffer pools 90*074ba9b2SJens Wiklander begat links to chains of buffer pools containing buffers, and lo the 91*074ba9b2SJens Wiklander buffers and links and pools of buffers and pools of links to chains of 92*074ba9b2SJens Wiklander pools of buffers were fruitful and they multiplied and the Operating 93*074ba9b2SJens Wiklander System looked down upon them and said that it was Good. 94*074ba9b2SJens Wiklander 95*074ba9b2SJens Wiklander 96*074ba9b2SJens Wiklander INTRODUCTION 97*074ba9b2SJens Wiklander ============ 98*074ba9b2SJens Wiklander 99*074ba9b2SJens Wiklander BGET is a comprehensive memory allocation package which is easily 100*074ba9b2SJens Wiklander configured to the needs of an application. BGET is efficient in 101*074ba9b2SJens Wiklander both the time needed to allocate and release buffers and in the 102*074ba9b2SJens Wiklander memory overhead required for buffer pool management. It 103*074ba9b2SJens Wiklander automatically consolidates contiguous space to minimise 104*074ba9b2SJens Wiklander fragmentation. BGET is configured by compile-time definitions, 105*074ba9b2SJens Wiklander Major options include: 106*074ba9b2SJens Wiklander 107*074ba9b2SJens Wiklander * A built-in test program to exercise BGET and 108*074ba9b2SJens Wiklander demonstrate how the various functions are used. 109*074ba9b2SJens Wiklander 110*074ba9b2SJens Wiklander * Allocation by either the "first fit" or "best fit" 111*074ba9b2SJens Wiklander method. 112*074ba9b2SJens Wiklander 113*074ba9b2SJens Wiklander * Wiping buffers at release time to catch code which 114*074ba9b2SJens Wiklander references previously released storage. 115*074ba9b2SJens Wiklander 116*074ba9b2SJens Wiklander * Built-in routines to dump individual buffers or the 117*074ba9b2SJens Wiklander entire buffer pool. 118*074ba9b2SJens Wiklander 119*074ba9b2SJens Wiklander * Retrieval of allocation and pool size statistics. 120*074ba9b2SJens Wiklander 121*074ba9b2SJens Wiklander * Quantisation of buffer sizes to a power of two to 122*074ba9b2SJens Wiklander satisfy hardware alignment constraints. 123*074ba9b2SJens Wiklander 124*074ba9b2SJens Wiklander * Automatic pool compaction, growth, and shrinkage by 125*074ba9b2SJens Wiklander means of call-backs to user defined functions. 126*074ba9b2SJens Wiklander 127*074ba9b2SJens Wiklander Applications of BGET can range from storage management in 128*074ba9b2SJens Wiklander ROM-based embedded programs to providing the framework upon which 129*074ba9b2SJens Wiklander a multitasking system incorporating garbage collection is 130*074ba9b2SJens Wiklander constructed. BGET incorporates extensive internal consistency 131*074ba9b2SJens Wiklander checking using the <assert.h> mechanism; all these checks can be 132*074ba9b2SJens Wiklander turned off by compiling with NDEBUG defined, yielding a version of 133*074ba9b2SJens Wiklander BGET with minimal size and maximum speed. 134*074ba9b2SJens Wiklander 135*074ba9b2SJens Wiklander The basic algorithm underlying BGET has withstood the test of 136*074ba9b2SJens Wiklander time; more than 25 years have passed since the first 137*074ba9b2SJens Wiklander implementation of this code. And yet, it is substantially more 138*074ba9b2SJens Wiklander efficient than the native allocation schemes of many operating 139*074ba9b2SJens Wiklander systems: the Macintosh and Microsoft Windows to name two, on which 140*074ba9b2SJens Wiklander programs have obtained substantial speed-ups by layering BGET as 141*074ba9b2SJens Wiklander an application level memory manager atop the underlying system's. 142*074ba9b2SJens Wiklander 143*074ba9b2SJens Wiklander BGET has been implemented on the largest mainframes and the lowest 144*074ba9b2SJens Wiklander of microprocessors. It has served as the core for multitasking 145*074ba9b2SJens Wiklander operating systems, multi-thread applications, embedded software in 146*074ba9b2SJens Wiklander data network switching processors, and a host of C programs. And 147*074ba9b2SJens Wiklander while it has accreted flexibility and additional options over the 148*074ba9b2SJens Wiklander years, it remains fast, memory efficient, portable, and easy to 149*074ba9b2SJens Wiklander integrate into your program. 150*074ba9b2SJens Wiklander 151*074ba9b2SJens Wiklander 152*074ba9b2SJens Wiklander BGET IMPLEMENTATION ASSUMPTIONS 153*074ba9b2SJens Wiklander =============================== 154*074ba9b2SJens Wiklander 155*074ba9b2SJens Wiklander BGET is written in as portable a dialect of C as possible. The 156*074ba9b2SJens Wiklander only fundamental assumption about the underlying hardware 157*074ba9b2SJens Wiklander architecture is that memory is allocated is a linear array which 158*074ba9b2SJens Wiklander can be addressed as a vector of C "char" objects. On segmented 159*074ba9b2SJens Wiklander address space architectures, this generally means that BGET should 160*074ba9b2SJens Wiklander be used to allocate storage within a single segment (although some 161*074ba9b2SJens Wiklander compilers simulate linear address spaces on segmented 162*074ba9b2SJens Wiklander architectures). On segmented architectures, then, BGET buffer 163*074ba9b2SJens Wiklander pools may not be larger than a segment, but since BGET allows any 164*074ba9b2SJens Wiklander number of separate buffer pools, there is no limit on the total 165*074ba9b2SJens Wiklander storage which can be managed, only on the largest individual 166*074ba9b2SJens Wiklander object which can be allocated. Machines with a linear address 167*074ba9b2SJens Wiklander architecture, such as the VAX, 680x0, Sparc, MIPS, or the Intel 168*074ba9b2SJens Wiklander 80386 and above in native mode, may use BGET without restriction. 169*074ba9b2SJens Wiklander 170*074ba9b2SJens Wiklander 171*074ba9b2SJens Wiklander GETTING STARTED WITH BGET 172*074ba9b2SJens Wiklander ========================= 173*074ba9b2SJens Wiklander 174*074ba9b2SJens Wiklander Although BGET can be configured in a multitude of fashions, there 175*074ba9b2SJens Wiklander are three basic ways of working with BGET. The functions 176*074ba9b2SJens Wiklander mentioned below are documented in the following section. Please 177*074ba9b2SJens Wiklander excuse the forward references which are made in the interest of 178*074ba9b2SJens Wiklander providing a roadmap to guide you to the BGET functions you're 179*074ba9b2SJens Wiklander likely to need. 180*074ba9b2SJens Wiklander 181*074ba9b2SJens Wiklander Embedded Applications 182*074ba9b2SJens Wiklander --------------------- 183*074ba9b2SJens Wiklander 184*074ba9b2SJens Wiklander Embedded applications typically have a fixed area of memory 185*074ba9b2SJens Wiklander dedicated to buffer allocation (often in a separate RAM address 186*074ba9b2SJens Wiklander space distinct from the ROM that contains the executable code). 187*074ba9b2SJens Wiklander To use BGET in such an environment, simply call bpool() with the 188*074ba9b2SJens Wiklander start address and length of the buffer pool area in RAM, then 189*074ba9b2SJens Wiklander allocate buffers with bget() and release them with brel(). 190*074ba9b2SJens Wiklander Embedded applications with very limited RAM but abundant CPU speed 191*074ba9b2SJens Wiklander may benefit by configuring BGET for BestFit allocation (which is 192*074ba9b2SJens Wiklander usually not worth it in other environments). 193*074ba9b2SJens Wiklander 194*074ba9b2SJens Wiklander Malloc() Emulation 195*074ba9b2SJens Wiklander ------------------ 196*074ba9b2SJens Wiklander 197*074ba9b2SJens Wiklander If the C library malloc() function is too slow, not present in 198*074ba9b2SJens Wiklander your development environment (for example, an a native Windows or 199*074ba9b2SJens Wiklander Macintosh program), or otherwise unsuitable, you can replace it 200*074ba9b2SJens Wiklander with BGET. Initially define a buffer pool of an appropriate size 201*074ba9b2SJens Wiklander with bpool()--usually obtained by making a call to the operating 202*074ba9b2SJens Wiklander system's low-level memory allocator. Then allocate buffers with 203*074ba9b2SJens Wiklander bget(), bgetz(), and bgetr() (the last two permit the allocation 204*074ba9b2SJens Wiklander of buffers initialised to zero and [inefficient] re-allocation of 205*074ba9b2SJens Wiklander existing buffers for compatibility with C library functions). 206*074ba9b2SJens Wiklander Release buffers by calling brel(). If a buffer allocation request 207*074ba9b2SJens Wiklander fails, obtain more storage from the underlying operating system, 208*074ba9b2SJens Wiklander add it to the buffer pool by another call to bpool(), and continue 209*074ba9b2SJens Wiklander execution. 210*074ba9b2SJens Wiklander 211*074ba9b2SJens Wiklander Automatic Storage Management 212*074ba9b2SJens Wiklander ---------------------------- 213*074ba9b2SJens Wiklander 214*074ba9b2SJens Wiklander You can use BGET as your application's native memory manager and 215*074ba9b2SJens Wiklander implement automatic storage pool expansion, contraction, and 216*074ba9b2SJens Wiklander optionally application-specific memory compaction by compiling 217*074ba9b2SJens Wiklander BGET with the BECtl variable defined, then calling bectl() and 218*074ba9b2SJens Wiklander supplying functions for storage compaction, acquisition, and 219*074ba9b2SJens Wiklander release, as well as a standard pool expansion increment. All of 220*074ba9b2SJens Wiklander these functions are optional (although it doesn't make much sense 221*074ba9b2SJens Wiklander to provide a release function without an acquisition function, 222*074ba9b2SJens Wiklander does it?). Once the call-back functions have been defined with 223*074ba9b2SJens Wiklander bectl(), you simply use bget() and brel() to allocate and release 224*074ba9b2SJens Wiklander storage as before. You can supply an initial buffer pool with 225*074ba9b2SJens Wiklander bpool() or rely on automatic allocation to acquire the entire 226*074ba9b2SJens Wiklander pool. When a call on bget() cannot be satisfied, BGET first 227*074ba9b2SJens Wiklander checks if a compaction function has been supplied. If so, it is 228*074ba9b2SJens Wiklander called (with the space required to satisfy the allocation request 229*074ba9b2SJens Wiklander and a sequence number to allow the compaction routine to be called 230*074ba9b2SJens Wiklander successively without looping). If the compaction function is able 231*074ba9b2SJens Wiklander to free any storage (it needn't know whether the storage it freed 232*074ba9b2SJens Wiklander was adequate) it should return a nonzero value, whereupon BGET 233*074ba9b2SJens Wiklander will retry the allocation request and, if it fails again, call the 234*074ba9b2SJens Wiklander compaction function again with the next-higher sequence number. 235*074ba9b2SJens Wiklander 236*074ba9b2SJens Wiklander If the compaction function returns zero, indicating failure to 237*074ba9b2SJens Wiklander free space, or no compaction function is defined, BGET next tests 238*074ba9b2SJens Wiklander whether a non-NULL allocation function was supplied to bectl(). 239*074ba9b2SJens Wiklander If so, that function is called with an argument indicating how 240*074ba9b2SJens Wiklander many bytes of additional space are required. This will be the 241*074ba9b2SJens Wiklander standard pool expansion increment supplied in the call to bectl() 242*074ba9b2SJens Wiklander unless the original bget() call requested a buffer larger than 243*074ba9b2SJens Wiklander this; buffers larger than the standard pool block can be managed 244*074ba9b2SJens Wiklander "off the books" by BGET in this mode. If the allocation function 245*074ba9b2SJens Wiklander succeeds in obtaining the storage, it returns a pointer to the new 246*074ba9b2SJens Wiklander block and BGET expands the buffer pool; if it fails, the 247*074ba9b2SJens Wiklander allocation request fails and returns NULL to the caller. If a 248*074ba9b2SJens Wiklander non-NULL release function is supplied, expansion blocks which 249*074ba9b2SJens Wiklander become totally empty are released to the global free pool by 250*074ba9b2SJens Wiklander passing their addresses to the release function. 251*074ba9b2SJens Wiklander 252*074ba9b2SJens Wiklander Equipped with appropriate allocation, release, and compaction 253*074ba9b2SJens Wiklander functions, BGET can be used as part of very sophisticated memory 254*074ba9b2SJens Wiklander management strategies, including garbage collection. (Note, 255*074ba9b2SJens Wiklander however, that BGET is *not* a garbage collector by itself, and 256*074ba9b2SJens Wiklander that developing such a system requires much additional logic and 257*074ba9b2SJens Wiklander careful design of the application's memory allocation strategy.) 258*074ba9b2SJens Wiklander 259*074ba9b2SJens Wiklander 260*074ba9b2SJens Wiklander BGET FUNCTION DESCRIPTIONS 261*074ba9b2SJens Wiklander ========================== 262*074ba9b2SJens Wiklander 263*074ba9b2SJens Wiklander Functions implemented in this file (some are enabled by certain of 264*074ba9b2SJens Wiklander the optional settings below): 265*074ba9b2SJens Wiklander 266*074ba9b2SJens Wiklander void bpool(void *buffer, bufsize len); 267*074ba9b2SJens Wiklander 268*074ba9b2SJens Wiklander Create a buffer pool of <len> bytes, using the storage starting at 269*074ba9b2SJens Wiklander <buffer>. You can call bpool() subsequently to contribute 270*074ba9b2SJens Wiklander additional storage to the overall buffer pool. 271*074ba9b2SJens Wiklander 272*074ba9b2SJens Wiklander void *bget(bufsize size); 273*074ba9b2SJens Wiklander 274*074ba9b2SJens Wiklander Allocate a buffer of <size> bytes. The address of the buffer is 275*074ba9b2SJens Wiklander returned, or NULL if insufficient memory was available to allocate 276*074ba9b2SJens Wiklander the buffer. 277*074ba9b2SJens Wiklander 278*074ba9b2SJens Wiklander void *bgetz(bufsize size); 279*074ba9b2SJens Wiklander 280*074ba9b2SJens Wiklander Allocate a buffer of <size> bytes and clear it to all zeroes. The 281*074ba9b2SJens Wiklander address of the buffer is returned, or NULL if insufficient memory 282*074ba9b2SJens Wiklander was available to allocate the buffer. 283*074ba9b2SJens Wiklander 284*074ba9b2SJens Wiklander void *bgetr(void *buffer, bufsize newsize); 285*074ba9b2SJens Wiklander 286*074ba9b2SJens Wiklander Reallocate a buffer previously allocated by bget(), changing its 287*074ba9b2SJens Wiklander size to <newsize> and preserving all existing data. NULL is 288*074ba9b2SJens Wiklander returned if insufficient memory is available to reallocate the 289*074ba9b2SJens Wiklander buffer, in which case the original buffer remains intact. 290*074ba9b2SJens Wiklander 291*074ba9b2SJens Wiklander void brel(void *buf); 292*074ba9b2SJens Wiklander 293*074ba9b2SJens Wiklander Return the buffer <buf>, previously allocated by bget(), to the 294*074ba9b2SJens Wiklander free space pool. 295*074ba9b2SJens Wiklander 296*074ba9b2SJens Wiklander void bectl(int (*compact)(bufsize sizereq, int sequence), 297*074ba9b2SJens Wiklander void *(*acquire)(bufsize size), 298*074ba9b2SJens Wiklander void (*release)(void *buf), 299*074ba9b2SJens Wiklander bufsize pool_incr); 300*074ba9b2SJens Wiklander 301*074ba9b2SJens Wiklander Expansion control: specify functions through which the package may 302*074ba9b2SJens Wiklander compact storage (or take other appropriate action) when an 303*074ba9b2SJens Wiklander allocation request fails, and optionally automatically acquire 304*074ba9b2SJens Wiklander storage for expansion blocks when necessary, and release such 305*074ba9b2SJens Wiklander blocks when they become empty. If <compact> is non-NULL, whenever 306*074ba9b2SJens Wiklander a buffer allocation request fails, the <compact> function will be 307*074ba9b2SJens Wiklander called with arguments specifying the number of bytes (total buffer 308*074ba9b2SJens Wiklander size, including header overhead) required to satisfy the 309*074ba9b2SJens Wiklander allocation request, and a sequence number indicating the number of 310*074ba9b2SJens Wiklander consecutive calls on <compact> attempting to satisfy this 311*074ba9b2SJens Wiklander allocation request. The sequence number is 1 for the first call 312*074ba9b2SJens Wiklander on <compact> for a given allocation request, and increments on 313*074ba9b2SJens Wiklander subsequent calls, permitting the <compact> function to take 314*074ba9b2SJens Wiklander increasingly dire measures in an attempt to free up storage. If 315*074ba9b2SJens Wiklander the <compact> function returns a nonzero value, the allocation 316*074ba9b2SJens Wiklander attempt is re-tried. If <compact> returns 0 (as it must if it 317*074ba9b2SJens Wiklander isn't able to release any space or add storage to the buffer 318*074ba9b2SJens Wiklander pool), the allocation request fails, which can trigger automatic 319*074ba9b2SJens Wiklander pool expansion if the <acquire> argument is non-NULL. At the time 320*074ba9b2SJens Wiklander the <compact> function is called, the state of the buffer 321*074ba9b2SJens Wiklander allocator is identical to that at the moment the allocation 322*074ba9b2SJens Wiklander request was made; consequently, the <compact> function may call 323*074ba9b2SJens Wiklander brel(), bpool(), bstats(), and/or directly manipulate the buffer 324*074ba9b2SJens Wiklander pool in any manner which would be valid were the application in 325*074ba9b2SJens Wiklander control. This does not, however, relieve the <compact> function 326*074ba9b2SJens Wiklander of the need to ensure that whatever actions it takes do not change 327*074ba9b2SJens Wiklander things underneath the application that made the allocation 328*074ba9b2SJens Wiklander request. For example, a <compact> function that released a buffer 329*074ba9b2SJens Wiklander in the process of being reallocated with bgetr() would lead to 330*074ba9b2SJens Wiklander disaster. Implementing a safe and effective <compact> mechanism 331*074ba9b2SJens Wiklander requires careful design of an application's memory architecture, 332*074ba9b2SJens Wiklander and cannot generally be easily retrofitted into existing code. 333*074ba9b2SJens Wiklander 334*074ba9b2SJens Wiklander If <acquire> is non-NULL, that function will be called whenever an 335*074ba9b2SJens Wiklander allocation request fails. If the <acquire> function succeeds in 336*074ba9b2SJens Wiklander allocating the requested space and returns a pointer to the new 337*074ba9b2SJens Wiklander area, allocation will proceed using the expanded buffer pool. If 338*074ba9b2SJens Wiklander <acquire> cannot obtain the requested space, it should return NULL 339*074ba9b2SJens Wiklander and the entire allocation process will fail. <pool_incr> 340*074ba9b2SJens Wiklander specifies the normal expansion block size. Providing an <acquire> 341*074ba9b2SJens Wiklander function will cause subsequent bget() requests for buffers too 342*074ba9b2SJens Wiklander large to be managed in the linked-block scheme (in other words, 343*074ba9b2SJens Wiklander larger than <pool_incr> minus the buffer overhead) to be satisfied 344*074ba9b2SJens Wiklander directly by calls to the <acquire> function. Automatic release of 345*074ba9b2SJens Wiklander empty pool blocks will occur only if all pool blocks in the system 346*074ba9b2SJens Wiklander are the size given by <pool_incr>. 347*074ba9b2SJens Wiklander 348*074ba9b2SJens Wiklander void bstats(bufsize *curalloc, bufsize *totfree, 349*074ba9b2SJens Wiklander bufsize *maxfree, long *nget, long *nrel); 350*074ba9b2SJens Wiklander 351*074ba9b2SJens Wiklander The amount of space currently allocated is stored into the 352*074ba9b2SJens Wiklander variable pointed to by <curalloc>. The total free space (sum of 353*074ba9b2SJens Wiklander all free blocks in the pool) is stored into the variable pointed 354*074ba9b2SJens Wiklander to by <totfree>, and the size of the largest single block in the 355*074ba9b2SJens Wiklander free space pool is stored into the variable pointed to by 356*074ba9b2SJens Wiklander <maxfree>. The variables pointed to by <nget> and <nrel> are 357*074ba9b2SJens Wiklander filled, respectively, with the number of successful (non-NULL 358*074ba9b2SJens Wiklander return) bget() calls and the number of brel() calls. 359*074ba9b2SJens Wiklander 360*074ba9b2SJens Wiklander void bstatse(bufsize *pool_incr, long *npool, 361*074ba9b2SJens Wiklander long *npget, long *nprel, 362*074ba9b2SJens Wiklander long *ndget, long *ndrel); 363*074ba9b2SJens Wiklander 364*074ba9b2SJens Wiklander Extended statistics: The expansion block size will be stored into 365*074ba9b2SJens Wiklander the variable pointed to by <pool_incr>, or the negative thereof if 366*074ba9b2SJens Wiklander automatic expansion block releases are disabled. The number of 367*074ba9b2SJens Wiklander currently active pool blocks will be stored into the variable 368*074ba9b2SJens Wiklander pointed to by <npool>. The variables pointed to by <npget> and 369*074ba9b2SJens Wiklander <nprel> will be filled with, respectively, the number of expansion 370*074ba9b2SJens Wiklander block acquisitions and releases which have occurred. The 371*074ba9b2SJens Wiklander variables pointed to by <ndget> and <ndrel> will be filled with 372*074ba9b2SJens Wiklander the number of bget() and brel() calls, respectively, managed 373*074ba9b2SJens Wiklander through blocks directly allocated by the acquisition and release 374*074ba9b2SJens Wiklander functions. 375*074ba9b2SJens Wiklander 376*074ba9b2SJens Wiklander void bufdump(void *buf); 377*074ba9b2SJens Wiklander 378*074ba9b2SJens Wiklander The buffer pointed to by <buf> is dumped on standard output. 379*074ba9b2SJens Wiklander 380*074ba9b2SJens Wiklander void bpoold(void *pool, int dumpalloc, int dumpfree); 381*074ba9b2SJens Wiklander 382*074ba9b2SJens Wiklander All buffers in the buffer pool <pool>, previously initialised by a 383*074ba9b2SJens Wiklander call on bpool(), are listed in ascending memory address order. If 384*074ba9b2SJens Wiklander <dumpalloc> is nonzero, the contents of allocated buffers are 385*074ba9b2SJens Wiklander dumped; if <dumpfree> is nonzero, the contents of free blocks are 386*074ba9b2SJens Wiklander dumped. 387*074ba9b2SJens Wiklander 388*074ba9b2SJens Wiklander int bpoolv(void *pool); 389*074ba9b2SJens Wiklander 390*074ba9b2SJens Wiklander The named buffer pool, previously initialised by a call on 391*074ba9b2SJens Wiklander bpool(), is validated for bad pointers, overwritten data, etc. If 392*074ba9b2SJens Wiklander compiled with NDEBUG not defined, any error generates an assertion 393*074ba9b2SJens Wiklander failure. Otherwise 1 is returned if the pool is valid, 0 if an 394*074ba9b2SJens Wiklander error is found. 395*074ba9b2SJens Wiklander 396*074ba9b2SJens Wiklander 397*074ba9b2SJens Wiklander BGET CONFIGURATION 398*074ba9b2SJens Wiklander ================== 399*074ba9b2SJens Wiklander */ 400*074ba9b2SJens Wiklander 401*074ba9b2SJens Wiklander /* 402*074ba9b2SJens Wiklander * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED 403*074ba9b2SJens Wiklander * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 404*074ba9b2SJens Wiklander * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 405*074ba9b2SJens Wiklander * IN NO EVENT SHALL ST BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 406*074ba9b2SJens Wiklander * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 407*074ba9b2SJens Wiklander * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 408*074ba9b2SJens Wiklander * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 409*074ba9b2SJens Wiklander * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 410*074ba9b2SJens Wiklander * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 411*074ba9b2SJens Wiklander * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 412*074ba9b2SJens Wiklander */ 413*074ba9b2SJens Wiklander 414*074ba9b2SJens Wiklander /* #define BGET_ENABLE_ALL_OPTIONS */ 415*074ba9b2SJens Wiklander #ifdef BGET_ENABLE_OPTION 416*074ba9b2SJens Wiklander #define TestProg 20000 /* Generate built-in test program 417*074ba9b2SJens Wiklander if defined. The value specifies 418*074ba9b2SJens Wiklander how many buffer allocation attempts 419*074ba9b2SJens Wiklander the test program should make. */ 420*074ba9b2SJens Wiklander 421*074ba9b2SJens Wiklander #define SizeQuant 4 /* Buffer allocation size quantum: 422*074ba9b2SJens Wiklander all buffers allocated are a 423*074ba9b2SJens Wiklander multiple of this size. This 424*074ba9b2SJens Wiklander MUST be a power of two. */ 425*074ba9b2SJens Wiklander 426*074ba9b2SJens Wiklander #define BufDump 1 /* Define this symbol to enable the 427*074ba9b2SJens Wiklander bpoold() function which dumps the 428*074ba9b2SJens Wiklander buffers in a buffer pool. */ 429*074ba9b2SJens Wiklander 430*074ba9b2SJens Wiklander #define BufValid 1 /* Define this symbol to enable the 431*074ba9b2SJens Wiklander bpoolv() function for validating 432*074ba9b2SJens Wiklander a buffer pool. */ 433*074ba9b2SJens Wiklander 434*074ba9b2SJens Wiklander #define DumpData 1 /* Define this symbol to enable the 435*074ba9b2SJens Wiklander bufdump() function which allows 436*074ba9b2SJens Wiklander dumping the contents of an allocated 437*074ba9b2SJens Wiklander or free buffer. */ 438*074ba9b2SJens Wiklander 439*074ba9b2SJens Wiklander #define BufStats 1 /* Define this symbol to enable the 440*074ba9b2SJens Wiklander bstats() function which calculates 441*074ba9b2SJens Wiklander the total free space in the buffer 442*074ba9b2SJens Wiklander pool, the largest available 443*074ba9b2SJens Wiklander buffer, and the total space 444*074ba9b2SJens Wiklander currently allocated. */ 445*074ba9b2SJens Wiklander 446*074ba9b2SJens Wiklander #define FreeWipe 1 /* Wipe free buffers to a guaranteed 447*074ba9b2SJens Wiklander pattern of garbage to trip up 448*074ba9b2SJens Wiklander miscreants who attempt to use 449*074ba9b2SJens Wiklander pointers into released buffers. */ 450*074ba9b2SJens Wiklander 451*074ba9b2SJens Wiklander #define BestFit 1 /* Use a best fit algorithm when 452*074ba9b2SJens Wiklander searching for space for an 453*074ba9b2SJens Wiklander allocation request. This uses 454*074ba9b2SJens Wiklander memory more efficiently, but 455*074ba9b2SJens Wiklander allocation will be much slower. */ 456*074ba9b2SJens Wiklander 457*074ba9b2SJens Wiklander #define BECtl 1 /* Define this symbol to enable the 458*074ba9b2SJens Wiklander bectl() function for automatic 459*074ba9b2SJens Wiklander pool space control. */ 460*074ba9b2SJens Wiklander #endif 461*074ba9b2SJens Wiklander 462*074ba9b2SJens Wiklander #include <stdio.h> 463*074ba9b2SJens Wiklander 464*074ba9b2SJens Wiklander #ifdef lint 465*074ba9b2SJens Wiklander #define NDEBUG /* Exits in asserts confuse lint */ 466*074ba9b2SJens Wiklander /* LINTLIBRARY */ /* Don't complain about def, no ref */ 467*074ba9b2SJens Wiklander extern char *sprintf(); /* Sun includes don't define sprintf */ 468*074ba9b2SJens Wiklander #endif 469*074ba9b2SJens Wiklander 470*074ba9b2SJens Wiklander #include <assert.h> 471*074ba9b2SJens Wiklander #include <memory.h> 472*074ba9b2SJens Wiklander 473*074ba9b2SJens Wiklander #ifdef BufDump /* BufDump implies DumpData */ 474*074ba9b2SJens Wiklander #ifndef DumpData 475*074ba9b2SJens Wiklander #define DumpData 1 476*074ba9b2SJens Wiklander #endif 477*074ba9b2SJens Wiklander #endif 478*074ba9b2SJens Wiklander 479*074ba9b2SJens Wiklander #ifdef DumpData 480*074ba9b2SJens Wiklander #include <ctype.h> 481*074ba9b2SJens Wiklander #endif 482*074ba9b2SJens Wiklander 483*074ba9b2SJens Wiklander /* Declare the interface, including the requested buffer size type, 484*074ba9b2SJens Wiklander bufsize. */ 485*074ba9b2SJens Wiklander 486*074ba9b2SJens Wiklander #include "bget.h" 487*074ba9b2SJens Wiklander 488*074ba9b2SJens Wiklander #define MemSize int /* Type for size arguments to memxxx() 489*074ba9b2SJens Wiklander functions such as memcmp(). */ 490*074ba9b2SJens Wiklander 491*074ba9b2SJens Wiklander /* Queue links */ 492*074ba9b2SJens Wiklander 493*074ba9b2SJens Wiklander struct qlinks { 494*074ba9b2SJens Wiklander struct bfhead *flink; /* Forward link */ 495*074ba9b2SJens Wiklander struct bfhead *blink; /* Backward link */ 496*074ba9b2SJens Wiklander }; 497*074ba9b2SJens Wiklander 498*074ba9b2SJens Wiklander /* Header in allocated and free buffers */ 499*074ba9b2SJens Wiklander 500*074ba9b2SJens Wiklander struct bhead { 501*074ba9b2SJens Wiklander bufsize prevfree; /* Relative link back to previous 502*074ba9b2SJens Wiklander free buffer in memory or 0 if 503*074ba9b2SJens Wiklander previous buffer is allocated. */ 504*074ba9b2SJens Wiklander bufsize bsize; /* Buffer size: positive if free, 505*074ba9b2SJens Wiklander negative if allocated. */ 506*074ba9b2SJens Wiklander }; 507*074ba9b2SJens Wiklander #define BH(p) ((struct bhead *) (p)) 508*074ba9b2SJens Wiklander 509*074ba9b2SJens Wiklander /* Header in directly allocated buffers (by acqfcn) */ 510*074ba9b2SJens Wiklander 511*074ba9b2SJens Wiklander struct bdhead { 512*074ba9b2SJens Wiklander bufsize tsize; /* Total size, including overhead */ 513*074ba9b2SJens Wiklander struct bhead bh; /* Common header */ 514*074ba9b2SJens Wiklander }; 515*074ba9b2SJens Wiklander #define BDH(p) ((struct bdhead *) (p)) 516*074ba9b2SJens Wiklander 517*074ba9b2SJens Wiklander /* Header in free buffers */ 518*074ba9b2SJens Wiklander 519*074ba9b2SJens Wiklander struct bfhead { 520*074ba9b2SJens Wiklander struct bhead bh; /* Common allocated/free header */ 521*074ba9b2SJens Wiklander struct qlinks ql; /* Links on free list */ 522*074ba9b2SJens Wiklander }; 523*074ba9b2SJens Wiklander #define BFH(p) ((struct bfhead *) (p)) 524*074ba9b2SJens Wiklander 525*074ba9b2SJens Wiklander static struct bfhead freelist = { /* List of free buffers */ 526*074ba9b2SJens Wiklander {0, 0}, 527*074ba9b2SJens Wiklander {&freelist, &freelist} 528*074ba9b2SJens Wiklander }; 529*074ba9b2SJens Wiklander 530*074ba9b2SJens Wiklander 531*074ba9b2SJens Wiklander #ifdef BufStats 532*074ba9b2SJens Wiklander static bufsize totalloc = 0; /* Total space currently allocated */ 533*074ba9b2SJens Wiklander static long numget = 0, numrel = 0; /* Number of bget() and brel() calls */ 534*074ba9b2SJens Wiklander #ifdef BECtl 535*074ba9b2SJens Wiklander static long numpblk = 0; /* Number of pool blocks */ 536*074ba9b2SJens Wiklander static long numpget = 0, numprel = 0; /* Number of block gets and rels */ 537*074ba9b2SJens Wiklander static long numdget = 0, numdrel = 0; /* Number of direct gets and rels */ 538*074ba9b2SJens Wiklander #endif /* BECtl */ 539*074ba9b2SJens Wiklander #endif /* BufStats */ 540*074ba9b2SJens Wiklander 541*074ba9b2SJens Wiklander #ifdef BECtl 542*074ba9b2SJens Wiklander 543*074ba9b2SJens Wiklander /* Automatic expansion block management functions */ 544*074ba9b2SJens Wiklander 545*074ba9b2SJens Wiklander static int (*compfcn) _((bufsize sizereq, int sequence)) = NULL; 546*074ba9b2SJens Wiklander static void *(*acqfcn) _((bufsize size)) = NULL; 547*074ba9b2SJens Wiklander static void (*relfcn) _((void *buf)) = NULL; 548*074ba9b2SJens Wiklander 549*074ba9b2SJens Wiklander static bufsize exp_incr = 0; /* Expansion block size */ 550*074ba9b2SJens Wiklander static bufsize pool_len = 0; /* 0: no bpool calls have been made 551*074ba9b2SJens Wiklander -1: not all pool blocks are 552*074ba9b2SJens Wiklander the same size 553*074ba9b2SJens Wiklander >0: (common) block size for all 554*074ba9b2SJens Wiklander bpool calls made so far 555*074ba9b2SJens Wiklander */ 556*074ba9b2SJens Wiklander #endif 557*074ba9b2SJens Wiklander 558*074ba9b2SJens Wiklander /* Minimum allocation quantum: */ 559*074ba9b2SJens Wiklander 560*074ba9b2SJens Wiklander #define QLSize (sizeof(struct qlinks)) 561*074ba9b2SJens Wiklander #define SizeQ ((SizeQuant > QLSize) ? SizeQuant : QLSize) 562*074ba9b2SJens Wiklander 563*074ba9b2SJens Wiklander #define V (void) /* To denote unwanted returned values */ 564*074ba9b2SJens Wiklander 565*074ba9b2SJens Wiklander /* End sentinel: value placed in bsize field of dummy block delimiting 566*074ba9b2SJens Wiklander end of pool block. The most negative number which will fit in a 567*074ba9b2SJens Wiklander bufsize, defined in a way that the compiler will accept. */ 568*074ba9b2SJens Wiklander 569*074ba9b2SJens Wiklander #define ESent ((bufsize) (-(((1L << (sizeof(bufsize) * 8 - 2)) - 1) * 2) - 2)) 570*074ba9b2SJens Wiklander 571*074ba9b2SJens Wiklander /* BGET -- Allocate a buffer. */ 572*074ba9b2SJens Wiklander 573*074ba9b2SJens Wiklander void *bget(requested_size) 574*074ba9b2SJens Wiklander bufsize requested_size; 575*074ba9b2SJens Wiklander { 576*074ba9b2SJens Wiklander bufsize size = requested_size; 577*074ba9b2SJens Wiklander struct bfhead *b; 578*074ba9b2SJens Wiklander #ifdef BestFit 579*074ba9b2SJens Wiklander struct bfhead *best; 580*074ba9b2SJens Wiklander #endif 581*074ba9b2SJens Wiklander void *buf; 582*074ba9b2SJens Wiklander #ifdef BECtl 583*074ba9b2SJens Wiklander int compactseq = 0; 584*074ba9b2SJens Wiklander #endif 585*074ba9b2SJens Wiklander 586*074ba9b2SJens Wiklander assert(size > 0); 587*074ba9b2SJens Wiklander 588*074ba9b2SJens Wiklander if (size < SizeQ) { /* Need at least room for the */ 589*074ba9b2SJens Wiklander size = SizeQ; /* queue links. */ 590*074ba9b2SJens Wiklander } 591*074ba9b2SJens Wiklander #ifdef SizeQuant 592*074ba9b2SJens Wiklander #if SizeQuant > 1 593*074ba9b2SJens Wiklander size = (size + (SizeQuant - 1)) & (~(SizeQuant - 1)); 594*074ba9b2SJens Wiklander #endif 595*074ba9b2SJens Wiklander #endif 596*074ba9b2SJens Wiklander 597*074ba9b2SJens Wiklander size += sizeof(struct bhead); /* Add overhead in allocated buffer 598*074ba9b2SJens Wiklander to size required. */ 599*074ba9b2SJens Wiklander 600*074ba9b2SJens Wiklander #ifdef BECtl 601*074ba9b2SJens Wiklander /* If a compact function was provided in the call to bectl(), wrap 602*074ba9b2SJens Wiklander a loop around the allocation process to allow compaction to 603*074ba9b2SJens Wiklander intervene in case we don't find a suitable buffer in the chain. */ 604*074ba9b2SJens Wiklander 605*074ba9b2SJens Wiklander while (1) { 606*074ba9b2SJens Wiklander #endif 607*074ba9b2SJens Wiklander b = freelist.ql.flink; 608*074ba9b2SJens Wiklander #ifdef BestFit 609*074ba9b2SJens Wiklander best = &freelist; 610*074ba9b2SJens Wiklander #endif 611*074ba9b2SJens Wiklander 612*074ba9b2SJens Wiklander 613*074ba9b2SJens Wiklander /* Scan the free list searching for the first buffer big enough 614*074ba9b2SJens Wiklander to hold the requested size buffer. */ 615*074ba9b2SJens Wiklander 616*074ba9b2SJens Wiklander #ifdef BestFit 617*074ba9b2SJens Wiklander while (b != &freelist) { 618*074ba9b2SJens Wiklander if (b->bh.bsize >= size) { 619*074ba9b2SJens Wiklander if ((best == &freelist) || (b->bh.bsize < best->bh.bsize)) { 620*074ba9b2SJens Wiklander best = b; 621*074ba9b2SJens Wiklander } 622*074ba9b2SJens Wiklander } 623*074ba9b2SJens Wiklander b = b->ql.flink; /* Link to next buffer */ 624*074ba9b2SJens Wiklander } 625*074ba9b2SJens Wiklander b = best; 626*074ba9b2SJens Wiklander #endif /* BestFit */ 627*074ba9b2SJens Wiklander 628*074ba9b2SJens Wiklander while (b != &freelist) { 629*074ba9b2SJens Wiklander if ((bufsize) b->bh.bsize >= size) { 630*074ba9b2SJens Wiklander 631*074ba9b2SJens Wiklander /* Buffer is big enough to satisfy the request. Allocate it 632*074ba9b2SJens Wiklander to the caller. We must decide whether the buffer is large 633*074ba9b2SJens Wiklander enough to split into the part given to the caller and a 634*074ba9b2SJens Wiklander free buffer that remains on the free list, or whether the 635*074ba9b2SJens Wiklander entire buffer should be removed from the free list and 636*074ba9b2SJens Wiklander given to the caller in its entirety. We only split the 637*074ba9b2SJens Wiklander buffer if enough room remains for a header plus the minimum 638*074ba9b2SJens Wiklander quantum of allocation. */ 639*074ba9b2SJens Wiklander 640*074ba9b2SJens Wiklander if ((b->bh.bsize - size) > (SizeQ + (sizeof(struct bhead)))) { 641*074ba9b2SJens Wiklander struct bhead *ba, *bn; 642*074ba9b2SJens Wiklander 643*074ba9b2SJens Wiklander ba = BH(((char *) b) + (b->bh.bsize - size)); 644*074ba9b2SJens Wiklander bn = BH(((char *) ba) + size); 645*074ba9b2SJens Wiklander assert(bn->prevfree == b->bh.bsize); 646*074ba9b2SJens Wiklander /* Subtract size from length of free block. */ 647*074ba9b2SJens Wiklander b->bh.bsize -= size; 648*074ba9b2SJens Wiklander /* Link allocated buffer to the previous free buffer. */ 649*074ba9b2SJens Wiklander ba->prevfree = b->bh.bsize; 650*074ba9b2SJens Wiklander /* Plug negative size into user buffer. */ 651*074ba9b2SJens Wiklander ba->bsize = -(bufsize) size; 652*074ba9b2SJens Wiklander /* Mark buffer after this one not preceded by free block. */ 653*074ba9b2SJens Wiklander bn->prevfree = 0; 654*074ba9b2SJens Wiklander 655*074ba9b2SJens Wiklander #ifdef BufStats 656*074ba9b2SJens Wiklander totalloc += size; 657*074ba9b2SJens Wiklander numget++; /* Increment number of bget() calls */ 658*074ba9b2SJens Wiklander #endif 659*074ba9b2SJens Wiklander buf = (void *) ((((char *) ba) + sizeof(struct bhead))); 660*074ba9b2SJens Wiklander return buf; 661*074ba9b2SJens Wiklander } else { 662*074ba9b2SJens Wiklander struct bhead *ba; 663*074ba9b2SJens Wiklander 664*074ba9b2SJens Wiklander ba = BH(((char *) b) + b->bh.bsize); 665*074ba9b2SJens Wiklander assert(ba->prevfree == b->bh.bsize); 666*074ba9b2SJens Wiklander 667*074ba9b2SJens Wiklander /* The buffer isn't big enough to split. Give the whole 668*074ba9b2SJens Wiklander shebang to the caller and remove it from the free list. */ 669*074ba9b2SJens Wiklander 670*074ba9b2SJens Wiklander assert(b->ql.blink->ql.flink == b); 671*074ba9b2SJens Wiklander assert(b->ql.flink->ql.blink == b); 672*074ba9b2SJens Wiklander b->ql.blink->ql.flink = b->ql.flink; 673*074ba9b2SJens Wiklander b->ql.flink->ql.blink = b->ql.blink; 674*074ba9b2SJens Wiklander 675*074ba9b2SJens Wiklander #ifdef BufStats 676*074ba9b2SJens Wiklander totalloc += b->bh.bsize; 677*074ba9b2SJens Wiklander numget++; /* Increment number of bget() calls */ 678*074ba9b2SJens Wiklander #endif 679*074ba9b2SJens Wiklander /* Negate size to mark buffer allocated. */ 680*074ba9b2SJens Wiklander b->bh.bsize = -(b->bh.bsize); 681*074ba9b2SJens Wiklander 682*074ba9b2SJens Wiklander /* Zero the back pointer in the next buffer in memory 683*074ba9b2SJens Wiklander to indicate that this buffer is allocated. */ 684*074ba9b2SJens Wiklander ba->prevfree = 0; 685*074ba9b2SJens Wiklander 686*074ba9b2SJens Wiklander /* Give user buffer starting at queue links. */ 687*074ba9b2SJens Wiklander buf = (void *) &(b->ql); 688*074ba9b2SJens Wiklander return buf; 689*074ba9b2SJens Wiklander } 690*074ba9b2SJens Wiklander } 691*074ba9b2SJens Wiklander b = b->ql.flink; /* Link to next buffer */ 692*074ba9b2SJens Wiklander } 693*074ba9b2SJens Wiklander #ifdef BECtl 694*074ba9b2SJens Wiklander 695*074ba9b2SJens Wiklander /* We failed to find a buffer. If there's a compact function 696*074ba9b2SJens Wiklander defined, notify it of the size requested. If it returns 697*074ba9b2SJens Wiklander TRUE, try the allocation again. */ 698*074ba9b2SJens Wiklander 699*074ba9b2SJens Wiklander if ((compfcn == NULL) || (!(*compfcn)(size, ++compactseq))) { 700*074ba9b2SJens Wiklander break; 701*074ba9b2SJens Wiklander } 702*074ba9b2SJens Wiklander } 703*074ba9b2SJens Wiklander 704*074ba9b2SJens Wiklander /* No buffer available with requested size free. */ 705*074ba9b2SJens Wiklander 706*074ba9b2SJens Wiklander /* Don't give up yet -- look in the reserve supply. */ 707*074ba9b2SJens Wiklander 708*074ba9b2SJens Wiklander if (acqfcn != NULL) { 709*074ba9b2SJens Wiklander if (size > exp_incr - sizeof(struct bhead)) { 710*074ba9b2SJens Wiklander 711*074ba9b2SJens Wiklander /* Request is too large to fit in a single expansion 712*074ba9b2SJens Wiklander block. Try to satisy it by a direct buffer acquisition. */ 713*074ba9b2SJens Wiklander 714*074ba9b2SJens Wiklander struct bdhead *bdh; 715*074ba9b2SJens Wiklander 716*074ba9b2SJens Wiklander size += sizeof(struct bdhead) - sizeof(struct bhead); 717*074ba9b2SJens Wiklander if ((bdh = BDH((*acqfcn)((bufsize) size))) != NULL) { 718*074ba9b2SJens Wiklander 719*074ba9b2SJens Wiklander /* Mark the buffer special by setting the size field 720*074ba9b2SJens Wiklander of its header to zero. */ 721*074ba9b2SJens Wiklander bdh->bh.bsize = 0; 722*074ba9b2SJens Wiklander bdh->bh.prevfree = 0; 723*074ba9b2SJens Wiklander bdh->tsize = size; 724*074ba9b2SJens Wiklander #ifdef BufStats 725*074ba9b2SJens Wiklander totalloc += size; 726*074ba9b2SJens Wiklander numget++; /* Increment number of bget() calls */ 727*074ba9b2SJens Wiklander numdget++; /* Direct bget() call count */ 728*074ba9b2SJens Wiklander #endif 729*074ba9b2SJens Wiklander buf = (void *) (bdh + 1); 730*074ba9b2SJens Wiklander return buf; 731*074ba9b2SJens Wiklander } 732*074ba9b2SJens Wiklander 733*074ba9b2SJens Wiklander } else { 734*074ba9b2SJens Wiklander 735*074ba9b2SJens Wiklander /* Try to obtain a new expansion block */ 736*074ba9b2SJens Wiklander 737*074ba9b2SJens Wiklander void *newpool; 738*074ba9b2SJens Wiklander 739*074ba9b2SJens Wiklander if ((newpool = (*acqfcn)((bufsize) exp_incr)) != NULL) { 740*074ba9b2SJens Wiklander bpool(newpool, exp_incr); 741*074ba9b2SJens Wiklander buf = bget(requested_size); /* This can't, I say, can't 742*074ba9b2SJens Wiklander get into a loop. */ 743*074ba9b2SJens Wiklander return buf; 744*074ba9b2SJens Wiklander } 745*074ba9b2SJens Wiklander } 746*074ba9b2SJens Wiklander } 747*074ba9b2SJens Wiklander 748*074ba9b2SJens Wiklander /* Still no buffer available */ 749*074ba9b2SJens Wiklander 750*074ba9b2SJens Wiklander #endif /* BECtl */ 751*074ba9b2SJens Wiklander 752*074ba9b2SJens Wiklander return NULL; 753*074ba9b2SJens Wiklander } 754*074ba9b2SJens Wiklander 755*074ba9b2SJens Wiklander /* BGETZ -- Allocate a buffer and clear its contents to zero. We clear 756*074ba9b2SJens Wiklander the entire contents of the buffer to zero, not just the 757*074ba9b2SJens Wiklander region requested by the caller. */ 758*074ba9b2SJens Wiklander 759*074ba9b2SJens Wiklander void *bgetz(size) 760*074ba9b2SJens Wiklander bufsize size; 761*074ba9b2SJens Wiklander { 762*074ba9b2SJens Wiklander char *buf = (char *) bget(size); 763*074ba9b2SJens Wiklander 764*074ba9b2SJens Wiklander if (buf != NULL) { 765*074ba9b2SJens Wiklander struct bhead *b; 766*074ba9b2SJens Wiklander bufsize rsize; 767*074ba9b2SJens Wiklander 768*074ba9b2SJens Wiklander b = BH(buf - sizeof(struct bhead)); 769*074ba9b2SJens Wiklander rsize = -(b->bsize); 770*074ba9b2SJens Wiklander if (rsize == 0) { 771*074ba9b2SJens Wiklander struct bdhead *bd; 772*074ba9b2SJens Wiklander 773*074ba9b2SJens Wiklander bd = BDH(buf - sizeof(struct bdhead)); 774*074ba9b2SJens Wiklander rsize = bd->tsize - sizeof(struct bdhead); 775*074ba9b2SJens Wiklander } else { 776*074ba9b2SJens Wiklander rsize -= sizeof(struct bhead); 777*074ba9b2SJens Wiklander } 778*074ba9b2SJens Wiklander assert(rsize >= size); 779*074ba9b2SJens Wiklander V memset(buf, 0, (MemSize) rsize); 780*074ba9b2SJens Wiklander } 781*074ba9b2SJens Wiklander return ((void *) buf); 782*074ba9b2SJens Wiklander } 783*074ba9b2SJens Wiklander 784*074ba9b2SJens Wiklander /* BGETR -- Reallocate a buffer. This is a minimal implementation, 785*074ba9b2SJens Wiklander simply in terms of brel() and bget(). It could be 786*074ba9b2SJens Wiklander enhanced to allow the buffer to grow into adjacent free 787*074ba9b2SJens Wiklander blocks and to avoid moving data unnecessarily. */ 788*074ba9b2SJens Wiklander 789*074ba9b2SJens Wiklander void *bgetr(buf, size) 790*074ba9b2SJens Wiklander void *buf; 791*074ba9b2SJens Wiklander bufsize size; 792*074ba9b2SJens Wiklander { 793*074ba9b2SJens Wiklander void *nbuf; 794*074ba9b2SJens Wiklander bufsize osize; /* Old size of buffer */ 795*074ba9b2SJens Wiklander struct bhead *b; 796*074ba9b2SJens Wiklander 797*074ba9b2SJens Wiklander if ((nbuf = bget(size)) == NULL) { /* Acquire new buffer */ 798*074ba9b2SJens Wiklander return NULL; 799*074ba9b2SJens Wiklander } 800*074ba9b2SJens Wiklander if (buf == NULL) { 801*074ba9b2SJens Wiklander return nbuf; 802*074ba9b2SJens Wiklander } 803*074ba9b2SJens Wiklander b = BH(((char *) buf) - sizeof(struct bhead)); 804*074ba9b2SJens Wiklander osize = -b->bsize; 805*074ba9b2SJens Wiklander #ifdef BECtl 806*074ba9b2SJens Wiklander if (osize == 0) { 807*074ba9b2SJens Wiklander /* Buffer acquired directly through acqfcn. */ 808*074ba9b2SJens Wiklander struct bdhead *bd; 809*074ba9b2SJens Wiklander 810*074ba9b2SJens Wiklander bd = BDH(((char *) buf) - sizeof(struct bdhead)); 811*074ba9b2SJens Wiklander osize = bd->tsize - sizeof(struct bdhead); 812*074ba9b2SJens Wiklander } else 813*074ba9b2SJens Wiklander #endif 814*074ba9b2SJens Wiklander osize -= sizeof(struct bhead); 815*074ba9b2SJens Wiklander assert(osize > 0); 816*074ba9b2SJens Wiklander V memcpy((char *) nbuf, (char *) buf, /* Copy the data */ 817*074ba9b2SJens Wiklander (MemSize) ((size < osize) ? size : osize)); 818*074ba9b2SJens Wiklander brel(buf); 819*074ba9b2SJens Wiklander return nbuf; 820*074ba9b2SJens Wiklander } 821*074ba9b2SJens Wiklander 822*074ba9b2SJens Wiklander /* BREL -- Release a buffer. */ 823*074ba9b2SJens Wiklander 824*074ba9b2SJens Wiklander void brel(buf) 825*074ba9b2SJens Wiklander void *buf; 826*074ba9b2SJens Wiklander { 827*074ba9b2SJens Wiklander struct bfhead *b, *bn; 828*074ba9b2SJens Wiklander 829*074ba9b2SJens Wiklander b = BFH(((char *) buf) - sizeof(struct bhead)); 830*074ba9b2SJens Wiklander #ifdef BufStats 831*074ba9b2SJens Wiklander numrel++; /* Increment number of brel() calls */ 832*074ba9b2SJens Wiklander #endif 833*074ba9b2SJens Wiklander assert(buf != NULL); 834*074ba9b2SJens Wiklander 835*074ba9b2SJens Wiklander #ifdef BECtl 836*074ba9b2SJens Wiklander if (b->bh.bsize == 0) { /* Directly-acquired buffer? */ 837*074ba9b2SJens Wiklander struct bdhead *bdh; 838*074ba9b2SJens Wiklander 839*074ba9b2SJens Wiklander bdh = BDH(((char *) buf) - sizeof(struct bdhead)); 840*074ba9b2SJens Wiklander assert(b->bh.prevfree == 0); 841*074ba9b2SJens Wiklander #ifdef BufStats 842*074ba9b2SJens Wiklander totalloc -= bdh->tsize; 843*074ba9b2SJens Wiklander assert(totalloc >= 0); 844*074ba9b2SJens Wiklander numdrel++; /* Number of direct releases */ 845*074ba9b2SJens Wiklander #endif /* BufStats */ 846*074ba9b2SJens Wiklander #ifdef FreeWipe 847*074ba9b2SJens Wiklander V memset((char *) buf, 0x55, 848*074ba9b2SJens Wiklander (MemSize) (bdh->tsize - sizeof(struct bdhead))); 849*074ba9b2SJens Wiklander #endif /* FreeWipe */ 850*074ba9b2SJens Wiklander assert(relfcn != NULL); 851*074ba9b2SJens Wiklander (*relfcn)((void *) bdh); /* Release it directly. */ 852*074ba9b2SJens Wiklander return; 853*074ba9b2SJens Wiklander } 854*074ba9b2SJens Wiklander #endif /* BECtl */ 855*074ba9b2SJens Wiklander 856*074ba9b2SJens Wiklander /* Buffer size must be negative, indicating that the buffer is 857*074ba9b2SJens Wiklander allocated. */ 858*074ba9b2SJens Wiklander 859*074ba9b2SJens Wiklander if (b->bh.bsize >= 0) { 860*074ba9b2SJens Wiklander bn = NULL; 861*074ba9b2SJens Wiklander } 862*074ba9b2SJens Wiklander assert(b->bh.bsize < 0); 863*074ba9b2SJens Wiklander 864*074ba9b2SJens Wiklander /* Back pointer in next buffer must be zero, indicating the 865*074ba9b2SJens Wiklander same thing: */ 866*074ba9b2SJens Wiklander 867*074ba9b2SJens Wiklander assert(BH((char *) b - b->bh.bsize)->prevfree == 0); 868*074ba9b2SJens Wiklander 869*074ba9b2SJens Wiklander #ifdef BufStats 870*074ba9b2SJens Wiklander totalloc += b->bh.bsize; 871*074ba9b2SJens Wiklander assert(totalloc >= 0); 872*074ba9b2SJens Wiklander #endif 873*074ba9b2SJens Wiklander 874*074ba9b2SJens Wiklander /* If the back link is nonzero, the previous buffer is free. */ 875*074ba9b2SJens Wiklander 876*074ba9b2SJens Wiklander if (b->bh.prevfree != 0) { 877*074ba9b2SJens Wiklander 878*074ba9b2SJens Wiklander /* The previous buffer is free. Consolidate this buffer with it 879*074ba9b2SJens Wiklander by adding the length of this buffer to the previous free 880*074ba9b2SJens Wiklander buffer. Note that we subtract the size in the buffer being 881*074ba9b2SJens Wiklander released, since it's negative to indicate that the buffer is 882*074ba9b2SJens Wiklander allocated. */ 883*074ba9b2SJens Wiklander 884*074ba9b2SJens Wiklander register bufsize size = b->bh.bsize; 885*074ba9b2SJens Wiklander 886*074ba9b2SJens Wiklander /* Make the previous buffer the one we're working on. */ 887*074ba9b2SJens Wiklander assert(BH((char *) b - b->bh.prevfree)->bsize == b->bh.prevfree); 888*074ba9b2SJens Wiklander b = BFH(((char *) b) - b->bh.prevfree); 889*074ba9b2SJens Wiklander b->bh.bsize -= size; 890*074ba9b2SJens Wiklander } else { 891*074ba9b2SJens Wiklander 892*074ba9b2SJens Wiklander /* The previous buffer isn't allocated. Insert this buffer 893*074ba9b2SJens Wiklander on the free list as an isolated free block. */ 894*074ba9b2SJens Wiklander 895*074ba9b2SJens Wiklander assert(freelist.ql.blink->ql.flink == &freelist); 896*074ba9b2SJens Wiklander assert(freelist.ql.flink->ql.blink == &freelist); 897*074ba9b2SJens Wiklander b->ql.flink = &freelist; 898*074ba9b2SJens Wiklander b->ql.blink = freelist.ql.blink; 899*074ba9b2SJens Wiklander freelist.ql.blink = b; 900*074ba9b2SJens Wiklander b->ql.blink->ql.flink = b; 901*074ba9b2SJens Wiklander b->bh.bsize = -b->bh.bsize; 902*074ba9b2SJens Wiklander } 903*074ba9b2SJens Wiklander 904*074ba9b2SJens Wiklander /* Now we look at the next buffer in memory, located by advancing from 905*074ba9b2SJens Wiklander the start of this buffer by its size, to see if that buffer is 906*074ba9b2SJens Wiklander free. If it is, we combine this buffer with the next one in 907*074ba9b2SJens Wiklander memory, dechaining the second buffer from the free list. */ 908*074ba9b2SJens Wiklander 909*074ba9b2SJens Wiklander bn = BFH(((char *) b) + b->bh.bsize); 910*074ba9b2SJens Wiklander if (bn->bh.bsize > 0) { 911*074ba9b2SJens Wiklander 912*074ba9b2SJens Wiklander /* The buffer is free. Remove it from the free list and add 913*074ba9b2SJens Wiklander its size to that of our buffer. */ 914*074ba9b2SJens Wiklander 915*074ba9b2SJens Wiklander assert(BH((char *) bn + bn->bh.bsize)->prevfree == bn->bh.bsize); 916*074ba9b2SJens Wiklander assert(bn->ql.blink->ql.flink == bn); 917*074ba9b2SJens Wiklander assert(bn->ql.flink->ql.blink == bn); 918*074ba9b2SJens Wiklander bn->ql.blink->ql.flink = bn->ql.flink; 919*074ba9b2SJens Wiklander bn->ql.flink->ql.blink = bn->ql.blink; 920*074ba9b2SJens Wiklander b->bh.bsize += bn->bh.bsize; 921*074ba9b2SJens Wiklander 922*074ba9b2SJens Wiklander /* Finally, advance to the buffer that follows the newly 923*074ba9b2SJens Wiklander consolidated free block. We must set its backpointer to the 924*074ba9b2SJens Wiklander head of the consolidated free block. We know the next block 925*074ba9b2SJens Wiklander must be an allocated block because the process of recombination 926*074ba9b2SJens Wiklander guarantees that two free blocks will never be contiguous in 927*074ba9b2SJens Wiklander memory. */ 928*074ba9b2SJens Wiklander 929*074ba9b2SJens Wiklander bn = BFH(((char *) b) + b->bh.bsize); 930*074ba9b2SJens Wiklander } 931*074ba9b2SJens Wiklander #ifdef FreeWipe 932*074ba9b2SJens Wiklander V memset(((char *) b) + sizeof(struct bfhead), 0x55, 933*074ba9b2SJens Wiklander (MemSize) (b->bh.bsize - sizeof(struct bfhead))); 934*074ba9b2SJens Wiklander #endif 935*074ba9b2SJens Wiklander assert(bn->bh.bsize < 0); 936*074ba9b2SJens Wiklander 937*074ba9b2SJens Wiklander /* The next buffer is allocated. Set the backpointer in it to point 938*074ba9b2SJens Wiklander to this buffer; the previous free buffer in memory. */ 939*074ba9b2SJens Wiklander 940*074ba9b2SJens Wiklander bn->bh.prevfree = b->bh.bsize; 941*074ba9b2SJens Wiklander 942*074ba9b2SJens Wiklander #ifdef BECtl 943*074ba9b2SJens Wiklander 944*074ba9b2SJens Wiklander /* If a block-release function is defined, and this free buffer 945*074ba9b2SJens Wiklander constitutes the entire block, release it. Note that pool_len 946*074ba9b2SJens Wiklander is defined in such a way that the test will fail unless all 947*074ba9b2SJens Wiklander pool blocks are the same size. */ 948*074ba9b2SJens Wiklander 949*074ba9b2SJens Wiklander if (relfcn != NULL && 950*074ba9b2SJens Wiklander ((bufsize) b->bh.bsize) == (pool_len - sizeof(struct bhead))) { 951*074ba9b2SJens Wiklander 952*074ba9b2SJens Wiklander assert(b->bh.prevfree == 0); 953*074ba9b2SJens Wiklander assert(BH((char *) b + b->bh.bsize)->bsize == ESent); 954*074ba9b2SJens Wiklander assert(BH((char *) b + b->bh.bsize)->prevfree == b->bh.bsize); 955*074ba9b2SJens Wiklander /* Unlink the buffer from the free list */ 956*074ba9b2SJens Wiklander b->ql.blink->ql.flink = b->ql.flink; 957*074ba9b2SJens Wiklander b->ql.flink->ql.blink = b->ql.blink; 958*074ba9b2SJens Wiklander 959*074ba9b2SJens Wiklander (*relfcn)(b); 960*074ba9b2SJens Wiklander #ifdef BufStats 961*074ba9b2SJens Wiklander numprel++; /* Nr of expansion block releases */ 962*074ba9b2SJens Wiklander numpblk--; /* Total number of blocks */ 963*074ba9b2SJens Wiklander assert(numpblk == numpget - numprel); 964*074ba9b2SJens Wiklander #endif /* BufStats */ 965*074ba9b2SJens Wiklander } 966*074ba9b2SJens Wiklander #endif /* BECtl */ 967*074ba9b2SJens Wiklander } 968*074ba9b2SJens Wiklander 969*074ba9b2SJens Wiklander #ifdef BECtl 970*074ba9b2SJens Wiklander 971*074ba9b2SJens Wiklander /* BECTL -- Establish automatic pool expansion control */ 972*074ba9b2SJens Wiklander 973*074ba9b2SJens Wiklander void bectl(compact, acquire, release, pool_incr) 974*074ba9b2SJens Wiklander int (*compact) _((bufsize sizereq, int sequence)); 975*074ba9b2SJens Wiklander void *(*acquire) _((bufsize size)); 976*074ba9b2SJens Wiklander void (*release) _((void *buf)); 977*074ba9b2SJens Wiklander bufsize pool_incr; 978*074ba9b2SJens Wiklander { 979*074ba9b2SJens Wiklander compfcn = compact; 980*074ba9b2SJens Wiklander acqfcn = acquire; 981*074ba9b2SJens Wiklander relfcn = release; 982*074ba9b2SJens Wiklander exp_incr = pool_incr; 983*074ba9b2SJens Wiklander } 984*074ba9b2SJens Wiklander #endif 985*074ba9b2SJens Wiklander 986*074ba9b2SJens Wiklander /* BPOOL -- Add a region of memory to the buffer pool. */ 987*074ba9b2SJens Wiklander 988*074ba9b2SJens Wiklander void bpool(buf, len) 989*074ba9b2SJens Wiklander void *buf; 990*074ba9b2SJens Wiklander bufsize len; 991*074ba9b2SJens Wiklander { 992*074ba9b2SJens Wiklander struct bfhead *b = BFH(buf); 993*074ba9b2SJens Wiklander struct bhead *bn; 994*074ba9b2SJens Wiklander 995*074ba9b2SJens Wiklander #ifdef SizeQuant 996*074ba9b2SJens Wiklander len &= ~(SizeQuant - 1); 997*074ba9b2SJens Wiklander #endif 998*074ba9b2SJens Wiklander #ifdef BECtl 999*074ba9b2SJens Wiklander if (pool_len == 0) { 1000*074ba9b2SJens Wiklander pool_len = len; 1001*074ba9b2SJens Wiklander } else if (len != pool_len) { 1002*074ba9b2SJens Wiklander pool_len = -1; 1003*074ba9b2SJens Wiklander } 1004*074ba9b2SJens Wiklander #ifdef BufStats 1005*074ba9b2SJens Wiklander numpget++; /* Number of block acquisitions */ 1006*074ba9b2SJens Wiklander numpblk++; /* Number of blocks total */ 1007*074ba9b2SJens Wiklander assert(numpblk == numpget - numprel); 1008*074ba9b2SJens Wiklander #endif /* BufStats */ 1009*074ba9b2SJens Wiklander #endif /* BECtl */ 1010*074ba9b2SJens Wiklander 1011*074ba9b2SJens Wiklander /* Since the block is initially occupied by a single free buffer, 1012*074ba9b2SJens Wiklander it had better not be (much) larger than the largest buffer 1013*074ba9b2SJens Wiklander whose size we can store in bhead.bsize. */ 1014*074ba9b2SJens Wiklander 1015*074ba9b2SJens Wiklander assert(len - sizeof(struct bhead) <= -((bufsize) ESent + 1)); 1016*074ba9b2SJens Wiklander 1017*074ba9b2SJens Wiklander /* Clear the backpointer at the start of the block to indicate that 1018*074ba9b2SJens Wiklander there is no free block prior to this one. That blocks 1019*074ba9b2SJens Wiklander recombination when the first block in memory is released. */ 1020*074ba9b2SJens Wiklander 1021*074ba9b2SJens Wiklander b->bh.prevfree = 0; 1022*074ba9b2SJens Wiklander 1023*074ba9b2SJens Wiklander /* Chain the new block to the free list. */ 1024*074ba9b2SJens Wiklander 1025*074ba9b2SJens Wiklander assert(freelist.ql.blink->ql.flink == &freelist); 1026*074ba9b2SJens Wiklander assert(freelist.ql.flink->ql.blink == &freelist); 1027*074ba9b2SJens Wiklander b->ql.flink = &freelist; 1028*074ba9b2SJens Wiklander b->ql.blink = freelist.ql.blink; 1029*074ba9b2SJens Wiklander freelist.ql.blink = b; 1030*074ba9b2SJens Wiklander b->ql.blink->ql.flink = b; 1031*074ba9b2SJens Wiklander 1032*074ba9b2SJens Wiklander /* Create a dummy allocated buffer at the end of the pool. This dummy 1033*074ba9b2SJens Wiklander buffer is seen when a buffer at the end of the pool is released and 1034*074ba9b2SJens Wiklander blocks recombination of the last buffer with the dummy buffer at 1035*074ba9b2SJens Wiklander the end. The length in the dummy buffer is set to the largest 1036*074ba9b2SJens Wiklander negative number to denote the end of the pool for diagnostic 1037*074ba9b2SJens Wiklander routines (this specific value is not counted on by the actual 1038*074ba9b2SJens Wiklander allocation and release functions). */ 1039*074ba9b2SJens Wiklander 1040*074ba9b2SJens Wiklander len -= sizeof(struct bhead); 1041*074ba9b2SJens Wiklander b->bh.bsize = (bufsize) len; 1042*074ba9b2SJens Wiklander #ifdef FreeWipe 1043*074ba9b2SJens Wiklander V memset(((char *) b) + sizeof(struct bfhead), 0x55, 1044*074ba9b2SJens Wiklander (MemSize) (len - sizeof(struct bfhead))); 1045*074ba9b2SJens Wiklander #endif 1046*074ba9b2SJens Wiklander bn = BH(((char *) b) + len); 1047*074ba9b2SJens Wiklander bn->prevfree = (bufsize) len; 1048*074ba9b2SJens Wiklander /* Definition of ESent assumes two's complement! */ 1049*074ba9b2SJens Wiklander assert((~0) == -1); 1050*074ba9b2SJens Wiklander bn->bsize = ESent; 1051*074ba9b2SJens Wiklander } 1052*074ba9b2SJens Wiklander 1053*074ba9b2SJens Wiklander #ifdef BufStats 1054*074ba9b2SJens Wiklander 1055*074ba9b2SJens Wiklander /* BSTATS -- Return buffer allocation free space statistics. */ 1056*074ba9b2SJens Wiklander 1057*074ba9b2SJens Wiklander void bstats(curalloc, totfree, maxfree, nget, nrel) 1058*074ba9b2SJens Wiklander bufsize *curalloc, *totfree, *maxfree; 1059*074ba9b2SJens Wiklander long *nget, *nrel; 1060*074ba9b2SJens Wiklander { 1061*074ba9b2SJens Wiklander struct bfhead *b = freelist.ql.flink; 1062*074ba9b2SJens Wiklander 1063*074ba9b2SJens Wiklander *nget = numget; 1064*074ba9b2SJens Wiklander *nrel = numrel; 1065*074ba9b2SJens Wiklander *curalloc = totalloc; 1066*074ba9b2SJens Wiklander *totfree = 0; 1067*074ba9b2SJens Wiklander *maxfree = -1; 1068*074ba9b2SJens Wiklander while (b != &freelist) { 1069*074ba9b2SJens Wiklander assert(b->bh.bsize > 0); 1070*074ba9b2SJens Wiklander *totfree += b->bh.bsize; 1071*074ba9b2SJens Wiklander if (b->bh.bsize > *maxfree) { 1072*074ba9b2SJens Wiklander *maxfree = b->bh.bsize; 1073*074ba9b2SJens Wiklander } 1074*074ba9b2SJens Wiklander b = b->ql.flink; /* Link to next buffer */ 1075*074ba9b2SJens Wiklander } 1076*074ba9b2SJens Wiklander } 1077*074ba9b2SJens Wiklander 1078*074ba9b2SJens Wiklander #ifdef BECtl 1079*074ba9b2SJens Wiklander 1080*074ba9b2SJens Wiklander /* BSTATSE -- Return extended statistics */ 1081*074ba9b2SJens Wiklander 1082*074ba9b2SJens Wiklander void bstatse(pool_incr, npool, npget, nprel, ndget, ndrel) 1083*074ba9b2SJens Wiklander bufsize *pool_incr; 1084*074ba9b2SJens Wiklander long *npool, *npget, *nprel, *ndget, *ndrel; 1085*074ba9b2SJens Wiklander { 1086*074ba9b2SJens Wiklander *pool_incr = (pool_len < 0) ? -exp_incr : exp_incr; 1087*074ba9b2SJens Wiklander *npool = numpblk; 1088*074ba9b2SJens Wiklander *npget = numpget; 1089*074ba9b2SJens Wiklander *nprel = numprel; 1090*074ba9b2SJens Wiklander *ndget = numdget; 1091*074ba9b2SJens Wiklander *ndrel = numdrel; 1092*074ba9b2SJens Wiklander } 1093*074ba9b2SJens Wiklander #endif /* BECtl */ 1094*074ba9b2SJens Wiklander #endif /* BufStats */ 1095*074ba9b2SJens Wiklander 1096*074ba9b2SJens Wiklander #ifdef DumpData 1097*074ba9b2SJens Wiklander 1098*074ba9b2SJens Wiklander /* BUFDUMP -- Dump the data in a buffer. This is called with the user 1099*074ba9b2SJens Wiklander data pointer, and backs up to the buffer header. It will 1100*074ba9b2SJens Wiklander dump either a free block or an allocated one. */ 1101*074ba9b2SJens Wiklander 1102*074ba9b2SJens Wiklander void bufdump(buf) 1103*074ba9b2SJens Wiklander void *buf; 1104*074ba9b2SJens Wiklander { 1105*074ba9b2SJens Wiklander struct bfhead *b; 1106*074ba9b2SJens Wiklander unsigned char *bdump; 1107*074ba9b2SJens Wiklander bufsize bdlen; 1108*074ba9b2SJens Wiklander 1109*074ba9b2SJens Wiklander b = BFH(((char *) buf) - sizeof(struct bhead)); 1110*074ba9b2SJens Wiklander assert(b->bh.bsize != 0); 1111*074ba9b2SJens Wiklander if (b->bh.bsize < 0) { 1112*074ba9b2SJens Wiklander bdump = (unsigned char *) buf; 1113*074ba9b2SJens Wiklander bdlen = (-b->bh.bsize) - sizeof(struct bhead); 1114*074ba9b2SJens Wiklander } else { 1115*074ba9b2SJens Wiklander bdump = (unsigned char *) (((char *) b) + sizeof(struct bfhead)); 1116*074ba9b2SJens Wiklander bdlen = b->bh.bsize - sizeof(struct bfhead); 1117*074ba9b2SJens Wiklander } 1118*074ba9b2SJens Wiklander 1119*074ba9b2SJens Wiklander while (bdlen > 0) { 1120*074ba9b2SJens Wiklander int i, dupes = 0; 1121*074ba9b2SJens Wiklander bufsize l = bdlen; 1122*074ba9b2SJens Wiklander char bhex[50], bascii[20]; 1123*074ba9b2SJens Wiklander 1124*074ba9b2SJens Wiklander if (l > 16) { 1125*074ba9b2SJens Wiklander l = 16; 1126*074ba9b2SJens Wiklander } 1127*074ba9b2SJens Wiklander 1128*074ba9b2SJens Wiklander for (i = 0; i < l; i++) { 1129*074ba9b2SJens Wiklander V snprintf(bhex + i * 3, sizeof(bhex) - i * 3, "%02X ", 1130*074ba9b2SJens Wiklander bdump[i]); 1131*074ba9b2SJens Wiklander bascii[i] = isprint(bdump[i]) ? bdump[i] : ' '; 1132*074ba9b2SJens Wiklander } 1133*074ba9b2SJens Wiklander bascii[i] = 0; 1134*074ba9b2SJens Wiklander V printf("%-48s %s\n", bhex, bascii); 1135*074ba9b2SJens Wiklander bdump += l; 1136*074ba9b2SJens Wiklander bdlen -= l; 1137*074ba9b2SJens Wiklander while ((bdlen > 16) && (memcmp((char *) (bdump - 16), 1138*074ba9b2SJens Wiklander (char *) bdump, 16) == 0)) { 1139*074ba9b2SJens Wiklander dupes++; 1140*074ba9b2SJens Wiklander bdump += 16; 1141*074ba9b2SJens Wiklander bdlen -= 16; 1142*074ba9b2SJens Wiklander } 1143*074ba9b2SJens Wiklander if (dupes > 1) { 1144*074ba9b2SJens Wiklander V printf( 1145*074ba9b2SJens Wiklander " (%d lines [%d bytes] identical to above line skipped)\n", 1146*074ba9b2SJens Wiklander dupes, dupes * 16); 1147*074ba9b2SJens Wiklander } else if (dupes == 1) { 1148*074ba9b2SJens Wiklander bdump -= 16; 1149*074ba9b2SJens Wiklander bdlen += 16; 1150*074ba9b2SJens Wiklander } 1151*074ba9b2SJens Wiklander } 1152*074ba9b2SJens Wiklander } 1153*074ba9b2SJens Wiklander #endif 1154*074ba9b2SJens Wiklander 1155*074ba9b2SJens Wiklander #ifdef BufDump 1156*074ba9b2SJens Wiklander 1157*074ba9b2SJens Wiklander /* BPOOLD -- Dump a buffer pool. The buffer headers are always listed. 1158*074ba9b2SJens Wiklander If DUMPALLOC is nonzero, the contents of allocated buffers 1159*074ba9b2SJens Wiklander are dumped. If DUMPFREE is nonzero, free blocks are 1160*074ba9b2SJens Wiklander dumped as well. If FreeWipe checking is enabled, free 1161*074ba9b2SJens Wiklander blocks which have been clobbered will always be dumped. */ 1162*074ba9b2SJens Wiklander 1163*074ba9b2SJens Wiklander void bpoold(buf, dumpalloc, dumpfree) 1164*074ba9b2SJens Wiklander void *buf; 1165*074ba9b2SJens Wiklander int dumpalloc, dumpfree; 1166*074ba9b2SJens Wiklander { 1167*074ba9b2SJens Wiklander struct bfhead *b = BFH(buf); 1168*074ba9b2SJens Wiklander 1169*074ba9b2SJens Wiklander while (b->bh.bsize != ESent) { 1170*074ba9b2SJens Wiklander bufsize bs = b->bh.bsize; 1171*074ba9b2SJens Wiklander 1172*074ba9b2SJens Wiklander if (bs < 0) { 1173*074ba9b2SJens Wiklander bs = -bs; 1174*074ba9b2SJens Wiklander V printf("Allocated buffer: size %6ld bytes.\n", (long) bs); 1175*074ba9b2SJens Wiklander if (dumpalloc) { 1176*074ba9b2SJens Wiklander bufdump((void *) (((char *) b) + sizeof(struct bhead))); 1177*074ba9b2SJens Wiklander } 1178*074ba9b2SJens Wiklander } else { 1179*074ba9b2SJens Wiklander char *lerr = ""; 1180*074ba9b2SJens Wiklander 1181*074ba9b2SJens Wiklander assert(bs > 0); 1182*074ba9b2SJens Wiklander if ((b->ql.blink->ql.flink != b) || 1183*074ba9b2SJens Wiklander (b->ql.flink->ql.blink != b)) { 1184*074ba9b2SJens Wiklander lerr = " (Bad free list links)"; 1185*074ba9b2SJens Wiklander } 1186*074ba9b2SJens Wiklander V printf("Free block: size %6ld bytes.%s\n", 1187*074ba9b2SJens Wiklander (long) bs, lerr); 1188*074ba9b2SJens Wiklander #ifdef FreeWipe 1189*074ba9b2SJens Wiklander lerr = ((char *) b) + sizeof(struct bfhead); 1190*074ba9b2SJens Wiklander if ((bs > sizeof(struct bfhead)) && ((*lerr != 0x55) || 1191*074ba9b2SJens Wiklander (memcmp(lerr, lerr + 1, 1192*074ba9b2SJens Wiklander (MemSize) (bs - (sizeof(struct bfhead) + 1))) != 0))) { 1193*074ba9b2SJens Wiklander V printf( 1194*074ba9b2SJens Wiklander "(Contents of above free block have been overstored.)\n"); 1195*074ba9b2SJens Wiklander bufdump((void *) (((char *) b) + sizeof(struct bhead))); 1196*074ba9b2SJens Wiklander } else 1197*074ba9b2SJens Wiklander #endif 1198*074ba9b2SJens Wiklander if (dumpfree) { 1199*074ba9b2SJens Wiklander bufdump((void *) (((char *) b) + sizeof(struct bhead))); 1200*074ba9b2SJens Wiklander } 1201*074ba9b2SJens Wiklander } 1202*074ba9b2SJens Wiklander b = BFH(((char *) b) + bs); 1203*074ba9b2SJens Wiklander } 1204*074ba9b2SJens Wiklander } 1205*074ba9b2SJens Wiklander #endif /* BufDump */ 1206*074ba9b2SJens Wiklander 1207*074ba9b2SJens Wiklander #ifdef BufValid 1208*074ba9b2SJens Wiklander 1209*074ba9b2SJens Wiklander /* BPOOLV -- Validate a buffer pool. If NDEBUG isn't defined, 1210*074ba9b2SJens Wiklander any error generates an assertion failure. */ 1211*074ba9b2SJens Wiklander 1212*074ba9b2SJens Wiklander int bpoolv(buf) 1213*074ba9b2SJens Wiklander void *buf; 1214*074ba9b2SJens Wiklander { 1215*074ba9b2SJens Wiklander struct bfhead *b = BFH(buf); 1216*074ba9b2SJens Wiklander 1217*074ba9b2SJens Wiklander while (b->bh.bsize != ESent) { 1218*074ba9b2SJens Wiklander bufsize bs = b->bh.bsize; 1219*074ba9b2SJens Wiklander 1220*074ba9b2SJens Wiklander if (bs < 0) { 1221*074ba9b2SJens Wiklander bs = -bs; 1222*074ba9b2SJens Wiklander } else { 1223*074ba9b2SJens Wiklander const char *lerr = ""; 1224*074ba9b2SJens Wiklander 1225*074ba9b2SJens Wiklander assert(bs > 0); 1226*074ba9b2SJens Wiklander if (bs <= 0) { 1227*074ba9b2SJens Wiklander return 0; 1228*074ba9b2SJens Wiklander } 1229*074ba9b2SJens Wiklander if ((b->ql.blink->ql.flink != b) || 1230*074ba9b2SJens Wiklander (b->ql.flink->ql.blink != b)) { 1231*074ba9b2SJens Wiklander V printf("Free block: size %6ld bytes. (Bad free list links)\n", 1232*074ba9b2SJens Wiklander (long) bs); 1233*074ba9b2SJens Wiklander assert(0); 1234*074ba9b2SJens Wiklander return 0; 1235*074ba9b2SJens Wiklander } 1236*074ba9b2SJens Wiklander #ifdef FreeWipe 1237*074ba9b2SJens Wiklander lerr = ((char *) b) + sizeof(struct bfhead); 1238*074ba9b2SJens Wiklander if ((bs > sizeof(struct bfhead)) && ((*lerr != 0x55) || 1239*074ba9b2SJens Wiklander (memcmp(lerr, lerr + 1, 1240*074ba9b2SJens Wiklander (MemSize) (bs - (sizeof(struct bfhead) + 1))) != 0))) { 1241*074ba9b2SJens Wiklander V printf( 1242*074ba9b2SJens Wiklander "(Contents of above free block have been overstored.)\n"); 1243*074ba9b2SJens Wiklander bufdump((void *) (((char *) b) + sizeof(struct bhead))); 1244*074ba9b2SJens Wiklander assert(0); 1245*074ba9b2SJens Wiklander return 0; 1246*074ba9b2SJens Wiklander } 1247*074ba9b2SJens Wiklander #endif 1248*074ba9b2SJens Wiklander } 1249*074ba9b2SJens Wiklander b = BFH(((char *) b) + bs); 1250*074ba9b2SJens Wiklander } 1251*074ba9b2SJens Wiklander return 1; 1252*074ba9b2SJens Wiklander } 1253*074ba9b2SJens Wiklander #endif /* BufValid */ 1254*074ba9b2SJens Wiklander 1255*074ba9b2SJens Wiklander /***********************\ 1256*074ba9b2SJens Wiklander * * 1257*074ba9b2SJens Wiklander * Built-in test program * 1258*074ba9b2SJens Wiklander * * 1259*074ba9b2SJens Wiklander \***********************/ 1260*074ba9b2SJens Wiklander 1261*074ba9b2SJens Wiklander #ifdef TestProg 1262*074ba9b2SJens Wiklander 1263*074ba9b2SJens Wiklander #define Repeatable 1 /* Repeatable pseudorandom sequence */ 1264*074ba9b2SJens Wiklander /* If Repeatable is not defined, a 1265*074ba9b2SJens Wiklander time-seeded pseudorandom sequence 1266*074ba9b2SJens Wiklander is generated, exercising BGET with 1267*074ba9b2SJens Wiklander a different pattern of calls on each 1268*074ba9b2SJens Wiklander run. */ 1269*074ba9b2SJens Wiklander #define OUR_RAND /* Use our own built-in version of 1270*074ba9b2SJens Wiklander rand() to guarantee the test is 1271*074ba9b2SJens Wiklander 100% repeatable. */ 1272*074ba9b2SJens Wiklander 1273*074ba9b2SJens Wiklander #ifdef BECtl 1274*074ba9b2SJens Wiklander #define PoolSize 300000 /* Test buffer pool size */ 1275*074ba9b2SJens Wiklander #else 1276*074ba9b2SJens Wiklander #define PoolSize 50000 /* Test buffer pool size */ 1277*074ba9b2SJens Wiklander #endif 1278*074ba9b2SJens Wiklander #define ExpIncr 32768 /* Test expansion block size */ 1279*074ba9b2SJens Wiklander #define CompactTries 10 /* Maximum tries at compacting */ 1280*074ba9b2SJens Wiklander 1281*074ba9b2SJens Wiklander #define dumpAlloc 0 /* Dump allocated buffers ? */ 1282*074ba9b2SJens Wiklander #define dumpFree 0 /* Dump free buffers ? */ 1283*074ba9b2SJens Wiklander 1284*074ba9b2SJens Wiklander #ifndef Repeatable 1285*074ba9b2SJens Wiklander extern long time(); 1286*074ba9b2SJens Wiklander #endif 1287*074ba9b2SJens Wiklander 1288*074ba9b2SJens Wiklander extern char *malloc(); 1289*074ba9b2SJens Wiklander extern int free _((char *)); 1290*074ba9b2SJens Wiklander 1291*074ba9b2SJens Wiklander static char *bchain = NULL; /* Our private buffer chain */ 1292*074ba9b2SJens Wiklander static char *bp = NULL; /* Our initial buffer pool */ 1293*074ba9b2SJens Wiklander 1294*074ba9b2SJens Wiklander #include <math.h> 1295*074ba9b2SJens Wiklander 1296*074ba9b2SJens Wiklander #ifdef OUR_RAND 1297*074ba9b2SJens Wiklander 1298*074ba9b2SJens Wiklander static unsigned long int next = 1; 1299*074ba9b2SJens Wiklander 1300*074ba9b2SJens Wiklander /* Return next random integer */ 1301*074ba9b2SJens Wiklander 1302*074ba9b2SJens Wiklander int rand() 1303*074ba9b2SJens Wiklander { 1304*074ba9b2SJens Wiklander next = next * 1103515245L + 12345; 1305*074ba9b2SJens Wiklander return (unsigned int) (next / 65536L) % 32768L; 1306*074ba9b2SJens Wiklander } 1307*074ba9b2SJens Wiklander 1308*074ba9b2SJens Wiklander /* Set seed for random generator */ 1309*074ba9b2SJens Wiklander 1310*074ba9b2SJens Wiklander void srand(seed) 1311*074ba9b2SJens Wiklander unsigned int seed; 1312*074ba9b2SJens Wiklander { 1313*074ba9b2SJens Wiklander next = seed; 1314*074ba9b2SJens Wiklander } 1315*074ba9b2SJens Wiklander #endif 1316*074ba9b2SJens Wiklander 1317*074ba9b2SJens Wiklander /* STATS -- Edit statistics returned by bstats() or bstatse(). */ 1318*074ba9b2SJens Wiklander 1319*074ba9b2SJens Wiklander static void stats(when) 1320*074ba9b2SJens Wiklander char *when; 1321*074ba9b2SJens Wiklander { 1322*074ba9b2SJens Wiklander bufsize cural, totfree, maxfree; 1323*074ba9b2SJens Wiklander long nget, nfree; 1324*074ba9b2SJens Wiklander #ifdef BECtl 1325*074ba9b2SJens Wiklander bufsize pincr; 1326*074ba9b2SJens Wiklander long totblocks, npget, nprel, ndget, ndrel; 1327*074ba9b2SJens Wiklander #endif 1328*074ba9b2SJens Wiklander 1329*074ba9b2SJens Wiklander bstats(&cural, &totfree, &maxfree, &nget, &nfree); 1330*074ba9b2SJens Wiklander V printf( 1331*074ba9b2SJens Wiklander "%s: %ld gets, %ld releases. %ld in use, %ld free, largest = %ld\n", 1332*074ba9b2SJens Wiklander when, nget, nfree, (long) cural, (long) totfree, (long) maxfree); 1333*074ba9b2SJens Wiklander #ifdef BECtl 1334*074ba9b2SJens Wiklander bstatse(&pincr, &totblocks, &npget, &nprel, &ndget, &ndrel); 1335*074ba9b2SJens Wiklander V printf( 1336*074ba9b2SJens Wiklander " Blocks: size = %ld, %ld (%ld bytes) in use, %ld gets, %ld frees\n", 1337*074ba9b2SJens Wiklander (long)pincr, totblocks, pincr * totblocks, npget, nprel); 1338*074ba9b2SJens Wiklander V printf(" %ld direct gets, %ld direct frees\n", ndget, ndrel); 1339*074ba9b2SJens Wiklander #endif /* BECtl */ 1340*074ba9b2SJens Wiklander } 1341*074ba9b2SJens Wiklander 1342*074ba9b2SJens Wiklander #ifdef BECtl 1343*074ba9b2SJens Wiklander static int protect = 0; /* Disable compaction during bgetr() */ 1344*074ba9b2SJens Wiklander 1345*074ba9b2SJens Wiklander /* BCOMPACT -- Compaction call-back function. */ 1346*074ba9b2SJens Wiklander 1347*074ba9b2SJens Wiklander static int bcompact(bsize, seq) 1348*074ba9b2SJens Wiklander bufsize bsize; 1349*074ba9b2SJens Wiklander int seq; 1350*074ba9b2SJens Wiklander { 1351*074ba9b2SJens Wiklander #ifdef CompactTries 1352*074ba9b2SJens Wiklander char *bc = bchain; 1353*074ba9b2SJens Wiklander int i = rand() & 0x3; 1354*074ba9b2SJens Wiklander 1355*074ba9b2SJens Wiklander #ifdef COMPACTRACE 1356*074ba9b2SJens Wiklander V printf("Compaction requested. %ld bytes needed, sequence %d.\n", 1357*074ba9b2SJens Wiklander (long) bsize, seq); 1358*074ba9b2SJens Wiklander #endif 1359*074ba9b2SJens Wiklander 1360*074ba9b2SJens Wiklander if (protect || (seq > CompactTries)) { 1361*074ba9b2SJens Wiklander #ifdef COMPACTRACE 1362*074ba9b2SJens Wiklander V printf("Compaction gave up.\n"); 1363*074ba9b2SJens Wiklander #endif 1364*074ba9b2SJens Wiklander return 0; 1365*074ba9b2SJens Wiklander } 1366*074ba9b2SJens Wiklander 1367*074ba9b2SJens Wiklander /* Based on a random cast, release a random buffer in the list 1368*074ba9b2SJens Wiklander of allocated buffers. */ 1369*074ba9b2SJens Wiklander 1370*074ba9b2SJens Wiklander while (i > 0 && bc != NULL) { 1371*074ba9b2SJens Wiklander bc = *((char **) bc); 1372*074ba9b2SJens Wiklander i--; 1373*074ba9b2SJens Wiklander } 1374*074ba9b2SJens Wiklander if (bc != NULL) { 1375*074ba9b2SJens Wiklander char *fb; 1376*074ba9b2SJens Wiklander 1377*074ba9b2SJens Wiklander fb = *((char **) bc); 1378*074ba9b2SJens Wiklander if (fb != NULL) { 1379*074ba9b2SJens Wiklander *((char **) bc) = *((char **) fb); 1380*074ba9b2SJens Wiklander brel((void *) fb); 1381*074ba9b2SJens Wiklander return 1; 1382*074ba9b2SJens Wiklander } 1383*074ba9b2SJens Wiklander } 1384*074ba9b2SJens Wiklander 1385*074ba9b2SJens Wiklander #ifdef COMPACTRACE 1386*074ba9b2SJens Wiklander V printf("Compaction bailed out.\n"); 1387*074ba9b2SJens Wiklander #endif 1388*074ba9b2SJens Wiklander #endif /* CompactTries */ 1389*074ba9b2SJens Wiklander return 0; 1390*074ba9b2SJens Wiklander } 1391*074ba9b2SJens Wiklander 1392*074ba9b2SJens Wiklander /* BEXPAND -- Expand pool call-back function. */ 1393*074ba9b2SJens Wiklander 1394*074ba9b2SJens Wiklander static void *bexpand(size) 1395*074ba9b2SJens Wiklander bufsize size; 1396*074ba9b2SJens Wiklander { 1397*074ba9b2SJens Wiklander void *np = NULL; 1398*074ba9b2SJens Wiklander bufsize cural, totfree, maxfree; 1399*074ba9b2SJens Wiklander long nget, nfree; 1400*074ba9b2SJens Wiklander 1401*074ba9b2SJens Wiklander /* Don't expand beyond the total allocated size given by PoolSize. */ 1402*074ba9b2SJens Wiklander 1403*074ba9b2SJens Wiklander bstats(&cural, &totfree, &maxfree, &nget, &nfree); 1404*074ba9b2SJens Wiklander 1405*074ba9b2SJens Wiklander if (cural < PoolSize) { 1406*074ba9b2SJens Wiklander np = (void *) malloc((unsigned) size); 1407*074ba9b2SJens Wiklander } 1408*074ba9b2SJens Wiklander #ifdef EXPTRACE 1409*074ba9b2SJens Wiklander V printf("Expand pool by %ld -- %s.\n", (long) size, 1410*074ba9b2SJens Wiklander np == NULL ? "failed" : "succeeded"); 1411*074ba9b2SJens Wiklander #endif 1412*074ba9b2SJens Wiklander return np; 1413*074ba9b2SJens Wiklander } 1414*074ba9b2SJens Wiklander 1415*074ba9b2SJens Wiklander /* BSHRINK -- Shrink buffer pool call-back function. */ 1416*074ba9b2SJens Wiklander 1417*074ba9b2SJens Wiklander static void bshrink(buf) 1418*074ba9b2SJens Wiklander void *buf; 1419*074ba9b2SJens Wiklander { 1420*074ba9b2SJens Wiklander if (((char *) buf) == bp) { 1421*074ba9b2SJens Wiklander #ifdef EXPTRACE 1422*074ba9b2SJens Wiklander V printf("Initial pool released.\n"); 1423*074ba9b2SJens Wiklander #endif 1424*074ba9b2SJens Wiklander bp = NULL; 1425*074ba9b2SJens Wiklander } 1426*074ba9b2SJens Wiklander #ifdef EXPTRACE 1427*074ba9b2SJens Wiklander V printf("Shrink pool.\n"); 1428*074ba9b2SJens Wiklander #endif 1429*074ba9b2SJens Wiklander free((char *) buf); 1430*074ba9b2SJens Wiklander } 1431*074ba9b2SJens Wiklander 1432*074ba9b2SJens Wiklander #endif /* BECtl */ 1433*074ba9b2SJens Wiklander 1434*074ba9b2SJens Wiklander /* Restrict buffer requests to those large enough to contain our pointer and 1435*074ba9b2SJens Wiklander small enough for the CPU architecture. */ 1436*074ba9b2SJens Wiklander 1437*074ba9b2SJens Wiklander static bufsize blimit(bs) 1438*074ba9b2SJens Wiklander bufsize bs; 1439*074ba9b2SJens Wiklander { 1440*074ba9b2SJens Wiklander if (bs < sizeof(char *)) { 1441*074ba9b2SJens Wiklander bs = sizeof(char *); 1442*074ba9b2SJens Wiklander } 1443*074ba9b2SJens Wiklander 1444*074ba9b2SJens Wiklander /* This is written out in this ugly fashion because the 1445*074ba9b2SJens Wiklander cool expression in sizeof(int) that auto-configured 1446*074ba9b2SJens Wiklander to any length int befuddled some compilers. */ 1447*074ba9b2SJens Wiklander 1448*074ba9b2SJens Wiklander if (sizeof(int) == 2) { 1449*074ba9b2SJens Wiklander if (bs > 32767) { 1450*074ba9b2SJens Wiklander bs = 32767; 1451*074ba9b2SJens Wiklander } 1452*074ba9b2SJens Wiklander } else { 1453*074ba9b2SJens Wiklander if (bs > 200000) { 1454*074ba9b2SJens Wiklander bs = 200000; 1455*074ba9b2SJens Wiklander } 1456*074ba9b2SJens Wiklander } 1457*074ba9b2SJens Wiklander return bs; 1458*074ba9b2SJens Wiklander } 1459*074ba9b2SJens Wiklander 1460*074ba9b2SJens Wiklander int main() 1461*074ba9b2SJens Wiklander { 1462*074ba9b2SJens Wiklander int i; 1463*074ba9b2SJens Wiklander double x; 1464*074ba9b2SJens Wiklander 1465*074ba9b2SJens Wiklander /* Seed the random number generator. If Repeatable is defined, we 1466*074ba9b2SJens Wiklander always use the same seed. Otherwise, we seed from the clock to 1467*074ba9b2SJens Wiklander shake things up from run to run. */ 1468*074ba9b2SJens Wiklander 1469*074ba9b2SJens Wiklander #ifdef Repeatable 1470*074ba9b2SJens Wiklander V srand(1234); 1471*074ba9b2SJens Wiklander #else 1472*074ba9b2SJens Wiklander V srand((int) time((long *) NULL)); 1473*074ba9b2SJens Wiklander #endif 1474*074ba9b2SJens Wiklander 1475*074ba9b2SJens Wiklander /* Compute x such that pow(x, p) ranges between 1 and 4*ExpIncr as 1476*074ba9b2SJens Wiklander p ranges from 0 to ExpIncr-1, with a concentration in the lower 1477*074ba9b2SJens Wiklander numbers. */ 1478*074ba9b2SJens Wiklander 1479*074ba9b2SJens Wiklander x = 4.0 * ExpIncr; 1480*074ba9b2SJens Wiklander x = log(x); 1481*074ba9b2SJens Wiklander x = exp(log(4.0 * ExpIncr) / (ExpIncr - 1.0)); 1482*074ba9b2SJens Wiklander 1483*074ba9b2SJens Wiklander #ifdef BECtl 1484*074ba9b2SJens Wiklander bectl(bcompact, bexpand, bshrink, (bufsize) ExpIncr); 1485*074ba9b2SJens Wiklander bp = malloc(ExpIncr); 1486*074ba9b2SJens Wiklander assert(bp != NULL); 1487*074ba9b2SJens Wiklander bpool((void *) bp, (bufsize) ExpIncr); 1488*074ba9b2SJens Wiklander #else 1489*074ba9b2SJens Wiklander bp = malloc(PoolSize); 1490*074ba9b2SJens Wiklander assert(bp != NULL); 1491*074ba9b2SJens Wiklander bpool((void *) bp, (bufsize) PoolSize); 1492*074ba9b2SJens Wiklander #endif 1493*074ba9b2SJens Wiklander 1494*074ba9b2SJens Wiklander stats("Create pool"); 1495*074ba9b2SJens Wiklander V bpoolv((void *) bp); 1496*074ba9b2SJens Wiklander bpoold((void *) bp, dumpAlloc, dumpFree); 1497*074ba9b2SJens Wiklander 1498*074ba9b2SJens Wiklander for (i = 0; i < TestProg; i++) { 1499*074ba9b2SJens Wiklander char *cb; 1500*074ba9b2SJens Wiklander bufsize bs = pow(x, (double) (rand() & (ExpIncr - 1))); 1501*074ba9b2SJens Wiklander 1502*074ba9b2SJens Wiklander assert(bs <= (((bufsize) 4) * ExpIncr)); 1503*074ba9b2SJens Wiklander bs = blimit(bs); 1504*074ba9b2SJens Wiklander if (rand() & 0x400) { 1505*074ba9b2SJens Wiklander cb = (char *) bgetz(bs); 1506*074ba9b2SJens Wiklander } else { 1507*074ba9b2SJens Wiklander cb = (char *) bget(bs); 1508*074ba9b2SJens Wiklander } 1509*074ba9b2SJens Wiklander if (cb == NULL) { 1510*074ba9b2SJens Wiklander #ifdef EasyOut 1511*074ba9b2SJens Wiklander break; 1512*074ba9b2SJens Wiklander #else 1513*074ba9b2SJens Wiklander char *bc = bchain; 1514*074ba9b2SJens Wiklander 1515*074ba9b2SJens Wiklander if (bc != NULL) { 1516*074ba9b2SJens Wiklander char *fb; 1517*074ba9b2SJens Wiklander 1518*074ba9b2SJens Wiklander fb = *((char **) bc); 1519*074ba9b2SJens Wiklander if (fb != NULL) { 1520*074ba9b2SJens Wiklander *((char **) bc) = *((char **) fb); 1521*074ba9b2SJens Wiklander brel((void *) fb); 1522*074ba9b2SJens Wiklander } 1523*074ba9b2SJens Wiklander continue; 1524*074ba9b2SJens Wiklander } 1525*074ba9b2SJens Wiklander #endif 1526*074ba9b2SJens Wiklander } 1527*074ba9b2SJens Wiklander *((char **) cb) = (char *) bchain; 1528*074ba9b2SJens Wiklander bchain = cb; 1529*074ba9b2SJens Wiklander 1530*074ba9b2SJens Wiklander /* Based on a random cast, release a random buffer in the list 1531*074ba9b2SJens Wiklander of allocated buffers. */ 1532*074ba9b2SJens Wiklander 1533*074ba9b2SJens Wiklander if ((rand() & 0x10) == 0) { 1534*074ba9b2SJens Wiklander char *bc = bchain; 1535*074ba9b2SJens Wiklander int i = rand() & 0x3; 1536*074ba9b2SJens Wiklander 1537*074ba9b2SJens Wiklander while (i > 0 && bc != NULL) { 1538*074ba9b2SJens Wiklander bc = *((char **) bc); 1539*074ba9b2SJens Wiklander i--; 1540*074ba9b2SJens Wiklander } 1541*074ba9b2SJens Wiklander if (bc != NULL) { 1542*074ba9b2SJens Wiklander char *fb; 1543*074ba9b2SJens Wiklander 1544*074ba9b2SJens Wiklander fb = *((char **) bc); 1545*074ba9b2SJens Wiklander if (fb != NULL) { 1546*074ba9b2SJens Wiklander *((char **) bc) = *((char **) fb); 1547*074ba9b2SJens Wiklander brel((void *) fb); 1548*074ba9b2SJens Wiklander } 1549*074ba9b2SJens Wiklander } 1550*074ba9b2SJens Wiklander } 1551*074ba9b2SJens Wiklander 1552*074ba9b2SJens Wiklander /* Based on a random cast, reallocate a random buffer in the list 1553*074ba9b2SJens Wiklander to a random size */ 1554*074ba9b2SJens Wiklander 1555*074ba9b2SJens Wiklander if ((rand() & 0x20) == 0) { 1556*074ba9b2SJens Wiklander char *bc = bchain; 1557*074ba9b2SJens Wiklander int i = rand() & 0x3; 1558*074ba9b2SJens Wiklander 1559*074ba9b2SJens Wiklander while (i > 0 && bc != NULL) { 1560*074ba9b2SJens Wiklander bc = *((char **) bc); 1561*074ba9b2SJens Wiklander i--; 1562*074ba9b2SJens Wiklander } 1563*074ba9b2SJens Wiklander if (bc != NULL) { 1564*074ba9b2SJens Wiklander char *fb; 1565*074ba9b2SJens Wiklander 1566*074ba9b2SJens Wiklander fb = *((char **) bc); 1567*074ba9b2SJens Wiklander if (fb != NULL) { 1568*074ba9b2SJens Wiklander char *newb; 1569*074ba9b2SJens Wiklander 1570*074ba9b2SJens Wiklander bs = pow(x, (double) (rand() & (ExpIncr - 1))); 1571*074ba9b2SJens Wiklander bs = blimit(bs); 1572*074ba9b2SJens Wiklander #ifdef BECtl 1573*074ba9b2SJens Wiklander protect = 1; /* Protect against compaction */ 1574*074ba9b2SJens Wiklander #endif 1575*074ba9b2SJens Wiklander newb = (char *) bgetr((void *) fb, bs); 1576*074ba9b2SJens Wiklander #ifdef BECtl 1577*074ba9b2SJens Wiklander protect = 0; 1578*074ba9b2SJens Wiklander #endif 1579*074ba9b2SJens Wiklander if (newb != NULL) { 1580*074ba9b2SJens Wiklander *((char **) bc) = newb; 1581*074ba9b2SJens Wiklander } 1582*074ba9b2SJens Wiklander } 1583*074ba9b2SJens Wiklander } 1584*074ba9b2SJens Wiklander } 1585*074ba9b2SJens Wiklander } 1586*074ba9b2SJens Wiklander stats("\nAfter allocation"); 1587*074ba9b2SJens Wiklander if (bp != NULL) { 1588*074ba9b2SJens Wiklander V bpoolv((void *) bp); 1589*074ba9b2SJens Wiklander bpoold((void *) bp, dumpAlloc, dumpFree); 1590*074ba9b2SJens Wiklander } 1591*074ba9b2SJens Wiklander 1592*074ba9b2SJens Wiklander while (bchain != NULL) { 1593*074ba9b2SJens Wiklander char *buf = bchain; 1594*074ba9b2SJens Wiklander 1595*074ba9b2SJens Wiklander bchain = *((char **) buf); 1596*074ba9b2SJens Wiklander brel((void *) buf); 1597*074ba9b2SJens Wiklander } 1598*074ba9b2SJens Wiklander stats("\nAfter release"); 1599*074ba9b2SJens Wiklander #ifndef BECtl 1600*074ba9b2SJens Wiklander if (bp != NULL) { 1601*074ba9b2SJens Wiklander V bpoolv((void *) bp); 1602*074ba9b2SJens Wiklander bpoold((void *) bp, dumpAlloc, dumpFree); 1603*074ba9b2SJens Wiklander } 1604*074ba9b2SJens Wiklander #endif 1605*074ba9b2SJens Wiklander 1606*074ba9b2SJens Wiklander return 0; 1607*074ba9b2SJens Wiklander } 1608*074ba9b2SJens Wiklander #endif 1609