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