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