Updated footer comment
[yaz-moved-to-github.git] / src / nmem.c
1 /* This file is part of the YAZ toolkit.
2  * Copyright (C) 1995-2009 Index Data
3  * See the file LICENSE for details.
4  */
5
6 /**
7  * \file nmem.c
8  * \brief Implements Nibble Memory
9  *
10  * This is a simple and fairly wasteful little module for nibble memory
11  * allocation. Evemtually we'll put in something better.
12  *
13  * FIXME - it also has some semaphore stuff, and stuff to handle errno.
14  *         These should be moved to some other place!
15  */
16 #if HAVE_CONFIG_H
17 #include <config.h>
18 #endif
19
20 #include <assert.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <errno.h>
24 #include <stddef.h>
25 #include <yaz/xmalloc.h>
26 #include <yaz/nmem.h>
27 #include <yaz/log.h>
28
29 #ifdef WIN32
30 #include <windows.h>
31 #endif
32
33 #define NMEM_CHUNK (4*1024)
34
35 struct nmem_block
36 {
37     char *buf;              /* memory allocated in this block */
38     size_t size;            /* size of buf */
39     size_t top;             /* top of buffer */
40     struct nmem_block *next;
41 };
42
43 struct nmem_control
44 {
45     int total;
46     struct nmem_block *blocks;
47     struct nmem_control *next;
48 };
49
50 struct align {
51     char x;
52     union {
53         char c;
54         short s;
55         int i;
56         long l;
57 #if HAVE_LONG_LONG
58         long long ll;
59 #endif
60         float f;
61         double d;
62     } u;
63 };
64
65 #define NMEM_ALIGN (offsetof(struct align, u))
66
67 static int log_level = 0;
68 static int log_level_initialized = 0;
69
70 static void free_block(struct nmem_block *p)
71 {  
72     xfree(p->buf);
73     xfree(p);
74     if (log_level)
75         yaz_log (log_level, "nmem free_block p=%p", p);
76 }
77
78 /*
79  * acquire a block with a minimum of size free bytes.
80  */
81 static struct nmem_block *get_block(size_t size)
82 {
83     struct nmem_block *r;
84     size_t get = NMEM_CHUNK;
85
86     if (log_level)
87         yaz_log (log_level, "nmem get_block size=%ld", (long) size);
88
89     
90     if (get < size)
91         get = size;
92     if(log_level)
93         yaz_log (log_level, "nmem get_block alloc new block size=%ld",
94                  (long) get);
95     
96     r = (struct nmem_block *) xmalloc(sizeof(*r));
97     r->buf = (char *)xmalloc(r->size = get);
98     r->top = 0;
99     return r;
100 }
101
102 void nmem_reset(NMEM n)
103 {
104     struct nmem_block *t;
105     
106     yaz_log (log_level, "nmem_reset p=%p", n);
107     if (!n)
108         return;
109     while (n->blocks)
110     {
111         t = n->blocks;
112         n->blocks = n->blocks->next;
113         free_block(t);
114     }
115     n->total = 0;
116 }
117
118 void *nmem_malloc(NMEM n, int size)
119 {
120     struct nmem_block *p;
121     char *r;
122
123     if (!n)
124     {
125         yaz_log (YLOG_FATAL, "calling nmem_malloc with an null pointer");
126         abort ();
127     }
128     p = n->blocks;
129     if (!p || p->size < size + p->top)
130     {
131         p = get_block(size);
132         p->next = n->blocks;
133         n->blocks = p;
134     }
135     r = p->buf + p->top;
136     /* align size */
137     p->top += (size + (NMEM_ALIGN - 1)) & ~(NMEM_ALIGN - 1);
138     n->total += size;
139     return r;
140 }
141
142 int nmem_total(NMEM n)
143 {
144     return n->total;
145 }
146
147 NMEM nmem_create(void)
148 {
149     NMEM r;
150     if (!log_level_initialized)
151     {
152         log_level = yaz_log_module_level("nmem");
153         log_level_initialized = 1;
154     }
155     
156     r = (struct nmem_control *)xmalloc(sizeof(*r));
157
158     r->blocks = 0;
159     r->total = 0;
160     r->next = 0;
161
162     return r;
163 }
164
165 void nmem_destroy(NMEM n)
166 {
167     if (!n)
168         return;
169     
170     nmem_reset(n);
171     xfree(n);
172 }
173
174 void nmem_transfer (NMEM dst, NMEM src)
175 {
176     struct nmem_block *t;
177     while ((t = src->blocks))
178     {
179         src->blocks = t->next;
180         t->next = dst->blocks;
181         dst->blocks = t;
182     }
183     dst->total += src->total;
184     src->total = 0;
185 }
186
187 int yaz_errno(void)
188 {
189     return errno;
190 }
191
192 void yaz_set_errno(int v)
193 {
194     errno = v;
195 }
196
197 void yaz_strerror(char *buf, int max)
198 {
199 #ifdef WIN32
200     DWORD err;
201 #endif
202     char *cp;
203     if (!log_level_initialized)
204     {
205         log_level = yaz_log_module_level("nmem");
206         log_level_initialized = 1;
207     }
208     
209 #ifdef WIN32
210     err = GetLastError();
211     if (err)
212     {
213         FormatMessage(
214                 FORMAT_MESSAGE_FROM_SYSTEM,
215                 NULL,
216                 err,
217                 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), /* Default lang */
218                 (LPTSTR) buf,
219                 max-1,
220                 NULL);
221     }
222     else
223         *buf = '\0';
224 #else
225 /* UNIX */
226 #if HAVE_STRERROR_R
227     *buf = '\0';
228     strerror_r(errno, buf, max);
229     /* if buffer is unset - use strerror anyway (GLIBC bug) */
230     if (*buf == '\0')
231         strcpy(buf, strerror(yaz_errno()));
232 #else
233     strcpy(buf, strerror(yaz_errno()));
234 #endif
235 /* UNIX */
236 #endif
237     if ((cp = strrchr(buf, '\n')))
238         *cp = '\0';
239     if ((cp = strrchr(buf, '\r')))
240         *cp = '\0';
241 }
242 /*
243  * Local variables:
244  * c-basic-offset: 4
245  * c-file-style: "Stroustrup"
246  * indent-tabs-mode: nil
247  * End:
248  * vim: shiftwidth=4 tabstop=8 expandtab
249  */
250