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