xref: /optee_os/lib/libutils/isoc/bget.c (revision 074ba9b23d8a550fa8d8f23d673e4b6984de10ba)
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