5f55cee7acd9d003dfa3c46931c10434673a5cf9
[yaz-moved-to-github.git] / util / nmem.c
1 /*
2  * Copyright (c) 1995-1998, Index Data.
3  * See the file LICENSE for details.
4  * Sebastian Hammer, Adam Dickmeiss
5  *
6  * $Log: nmem.c,v $
7  * Revision 1.13  1998-10-19 15:24:21  adam
8  * New nmem utility, nmem_transfer, that transfer blocks from one
9  * NMEM to another.
10  *
11  * Revision 1.12  1998/10/13 16:00:18  adam
12  * Implemented nmem_critical_{enter,leave}.
13  *
14  * Revision 1.11  1998/08/21 14:13:36  adam
15  * Added GNU Configure script to build Makefiles.
16  *
17  * Revision 1.10  1998/07/20 12:35:57  adam
18  * Added more memory diagnostics (when NMEM_DEBUG is 1).
19  *
20  * Revision 1.9  1998/07/07 15:49:01  adam
21  * Reduced chunk size.
22  *
23  * Revision 1.8  1998/07/03 14:21:27  adam
24  * Added critical sections for pthreads-library. Thanks to Ian Ibbotson,
25  * Fretwell Downing Informatics.
26  *
27  * Revision 1.7  1998/02/11 11:53:36  adam
28  * Changed code so that it compiles as C++.
29  *
30  * Revision 1.6  1997/10/31 12:20:09  adam
31  * Improved memory debugging for xmalloc/nmem.c. References to NMEM
32  * instead of ODR in n ESPEC-1 handling in source d1_espec.c.
33  * Bug fix: missing fclose in data1_read_espec1.
34  *
35  * Revision 1.5  1997/10/06 09:09:52  adam
36  * Function mmem_exit releases memory used by the freelists.
37  *
38  * Revision 1.4  1997/09/29 07:12:50  adam
39  * NMEM thread safe. NMEM must be initialized before use (sigh) -
40  * routine nmem_init/nmem_exit implemented.
41  *
42  * Revision 1.3  1997/07/21 12:47:38  adam
43  * Moved definition of nmem_control and nmem_block.
44  *
45  * Revision 1.2  1995/12/13 13:44:37  quinn
46  * Modified Data1-system to use nmem
47  *
48  * Revision 1.1  1995/11/13  09:27:52  quinn
49  * Fiddling with the variant stuff.
50  *
51  *
52  */
53
54 /*
55  * This is a simple and fairly wasteful little module for nibble memory
56  * allocation. Evemtually we'll put in something better.
57  */
58
59 #include <xmalloc.h>
60 #include <nmem.h>
61 #include <log.h>
62 #ifdef WINDOWS
63 #include <windows.h>
64 #elif _REENTRANT
65
66 #if HAVE_PTHREAD_H
67 #include <pthread.h>
68 #elif HAVE_THREAD_H
69 #include <thread.h>
70 #endif
71
72 #endif
73
74 #define NMEM_CHUNK (4*1024)
75
76 #ifdef WINDOWS
77 static CRITICAL_SECTION critical_section;
78 #define NMEM_ENTER EnterCriticalSection(&critical_section)
79 #define NMEM_LEAVE LeaveCriticalSection(&critical_section)
80 #elif _REENTRANT
81 static pthread_mutex_t nmem_mutex = PTHREAD_MUTEX_INITIALIZER;
82 #define NMEM_ENTER pthread_mutex_lock(&nmem_mutex);
83 #define NMEM_LEAVE pthread_mutex_unlock(&nmem_mutex);
84 #else
85 #define NMEM_ENTER
86 #define NMEM_LEAVE
87 #endif
88
89 static nmem_block *freelist = NULL;        /* "global" freelists */
90 static nmem_control *cfreelist = NULL;
91 static int nmem_active_no = 0;
92
93 static void free_block(nmem_block *p)
94 {  
95     p->next = freelist;
96     freelist = p;
97 #if NMEM_DEBUG
98     logf (LOG_DEBUG, "nmem free_block p=%p", p);
99 #endif
100 }
101
102 /*
103  * acquire a block with a minimum of size free bytes.
104  */
105 static nmem_block *get_block(int size)
106 {
107     nmem_block *r, *l;
108
109 #if NMEM_DEBUG
110     logf (LOG_DEBUG, "nmem get_block size=%d", size);
111 #endif
112     for (r = freelist, l = 0; r; l = r, r = r->next)
113         if (r->size >= size)
114             break;
115     if (r)
116     {
117 #if NMEM_DEBUG
118         logf (LOG_DEBUG, "nmem get_block found free block p=%p", r);
119 #endif
120         if (l)
121             l->next = r->next;
122         else
123             freelist = r->next;
124     }
125     else
126     {
127         int get = NMEM_CHUNK;
128
129         if (get < size)
130             get = size;
131 #if NMEM_DEBUG
132         logf (LOG_DEBUG, "nmem get_block alloc new block size=%d", get);
133 #endif
134         r = (nmem_block *)xmalloc(sizeof(*r));
135         r->buf = (char *)xmalloc(r->size = get);
136     }
137     r->top = 0;
138     return r;
139 }
140
141 void nmem_reset(NMEM n)
142 {
143     nmem_block *t;
144
145 #if NMEM_DEBUG
146     logf (LOG_DEBUG, "nmem_reset p=%p", n);
147 #endif
148     if (!n)
149         return;
150     NMEM_ENTER;
151     while (n->blocks)
152     {
153         t = n->blocks;
154         n->blocks = n->blocks->next;
155         free_block(t);
156     }
157     n->total = 0;
158     NMEM_LEAVE;
159 }
160
161 #if NMEM_DEBUG
162 void *nmem_malloc_f (const char *file, int line, NMEM n, int size)
163 #else
164 void *nmem_malloc(NMEM n, int size)
165 #endif
166 {
167     struct nmem_block *p;
168     char *r;
169
170 #if NMEM_DEBUG
171     logf (LOG_DEBUG, "%s:%d: nmem_malloc p=%p size=%d", file, line,
172                      n, size);
173 #endif
174     if (!n)
175         return xmalloc(size);
176     NMEM_ENTER;
177     p = n->blocks;
178     if (!p || p->size - p->top < size)
179     {
180         p = get_block(size);
181         p->next = n->blocks;
182         n->blocks = p;
183     }
184     r = p->buf + p->top;
185     /* align size */
186     p->top += (size + (sizeof(long) - 1)) & ~(sizeof(long) - 1);
187     n->total += size;
188     NMEM_LEAVE;
189     return r;
190 }
191
192 int nmem_total(NMEM n)
193 {
194     return n->total;
195 }
196
197 #if NMEM_DEBUG
198 NMEM nmem_create_f(const char *file, int line)
199 #else
200 NMEM nmem_create(void)
201 #endif
202 {
203     NMEM r;
204     
205     NMEM_ENTER;
206     nmem_active_no++;
207     r = cfreelist;
208     if (r)
209         cfreelist = cfreelist->next;
210     else
211         r = (nmem_control *)xmalloc(sizeof(*r));
212     NMEM_LEAVE;
213
214 #if NMEM_DEBUG
215     logf (LOG_DEBUG, "%s:%d: nmem_create %d p=%p", file, line,
216                      nmem_active_no-1, r);
217 #endif
218     r->blocks = 0;
219     r->total = 0;
220     r->next = 0;
221     return r;
222 }
223
224 #if NMEM_DEBUG
225 void nmem_destroy_f(const char *file, int line, NMEM n)
226 #else
227 void nmem_destroy(NMEM n)
228 #endif
229 {
230     if (!n)
231         return;
232     nmem_reset(n);
233     NMEM_ENTER;
234     nmem_active_no--;
235     n->next = cfreelist;
236     cfreelist = n;
237     NMEM_LEAVE;
238 #if NMEM_DEBUG
239     logf (LOG_DEBUG, "%s:%d: nmem_destroy %d p=%p", file, line,
240                      nmem_active_no, n);
241 #endif
242 }
243
244 void nmem_transfer (NMEM dst, NMEM src)
245 {
246     nmem_block *t;
247     while ((t=src->blocks))
248     {
249         src->blocks = t->next;
250         t->next = dst->blocks;
251         dst->blocks = t;
252     }
253     dst->total += src->total;
254     src->total = 0;
255 }
256
257 void nmem_critical_enter (void)
258 {
259     NMEM_ENTER;
260 }
261
262 void nmem_critical_leave (void)
263 {
264     NMEM_LEAVE;
265 }
266
267 void nmem_init (void)
268 {
269 #ifdef WINDOWS
270     InitializeCriticalSection(&critical_section);
271 #endif
272     nmem_active_no = 0;
273     freelist = NULL;
274     cfreelist = NULL;
275 }
276
277 void nmem_exit (void)
278 {
279     while (freelist)
280     {
281         struct nmem_block *fl = freelist;
282         freelist = freelist->next;
283         xfree (fl->buf);
284         xfree (fl);
285     }
286     while (cfreelist)
287     {
288         struct nmem_control *cfl = cfreelist;
289         cfreelist = cfreelist->next;
290         xfree (cfl);
291     }
292 #ifdef WINDOWS
293     DeleteCriticalSection(&critical_section);
294 #endif
295 }
296