properly initialize slot_alloc in context objects
[imager.git] / context.c
1 #include "imageri.h"
2 #include <stdio.h>
3
4 static im_slot_t slot_count;
5 static im_slot_destroy_t *slot_destructors;
6 static i_mutex_t slot_mutex;
7
8 /*
9 =item im_context_new()
10
11 Create a new Imager context object.
12
13 =cut
14 */
15
16 im_context_t
17 im_context_new(void) {
18   im_context_t ctx = malloc(sizeof(im_context_struct));
19   int i;
20
21   if (!slot_mutex)
22     slot_mutex = i_mutex_new();
23
24   if (!ctx)
25     return NULL;
26   
27   ctx->error_sp = IM_ERROR_COUNT-1;
28   for (i = 0; i < IM_ERROR_COUNT; ++i) {
29     ctx->error_alloc[i] = 0;
30     ctx->error_stack[i].msg = NULL;
31     ctx->error_stack[i].code = 0;
32   }
33 #ifdef IMAGER_LOG
34   ctx->log_level = 0;
35   ctx->lg_file = NULL;
36 #endif
37   ctx->max_width = 0;
38   ctx->max_height = 0;
39   ctx->max_bytes = DEF_BYTES_LIMIT;
40
41   ctx->slot_alloc = slot_count;
42   ctx->slots = calloc(sizeof(void *), ctx->slot_alloc);
43   if (!ctx->slots) {
44     free(ctx);
45     return NULL;
46   }
47
48   ctx->refcount = 1;
49
50 #ifdef IMAGER_TRACE_CONTEXT
51   fprintf(stderr, "im_context: created %p\n", ctx);
52 #endif
53
54
55   return ctx;
56 }
57
58 /*
59 =item im_context_refinc(ctx, where)
60 X<im_context_refinc API>
61 =section Context objects
62 =synopsis im_context_refinc(aIMCTX, "a description");
63
64 Add a new reference to the context.
65
66 =cut
67 */
68
69 void
70 im_context_refinc(im_context_t ctx, const char *where) {
71   ++ctx->refcount;
72
73 #ifdef IMAGER_TRACE_CONTEXT
74   fprintf(stderr, "im_context:%s: refinc %p (count now %lu)\n", where,
75           ctx, (unsigned long)ctx->refcount);
76 #endif
77 }
78
79 /*
80 =item im_context_refdec(ctx, where)
81 X<im_context_refdec API>
82 =section Context objects
83 =synopsis im_context_refdec(aIMCTX, "a description");
84
85 Remove a reference to the context, releasing it if all references have
86 been removed.
87
88 =cut
89 */
90
91 void
92 im_context_refdec(im_context_t ctx, const char *where) {
93   int i;
94   im_slot_t slot;
95
96   im_assert(ctx->refcount > 0);
97
98   --ctx->refcount;
99
100 #ifdef IMAGER_TRACE_CONTEXT
101   fprintf(stderr, "im_context:%s: delete %p (count now %lu)\n", where,
102           ctx, (unsigned long)ctx->refcount);
103 #endif
104
105   if (ctx->refcount != 0)
106     return;
107
108   for (slot = 0; slot < ctx->slot_alloc; ++slot) {
109     if (ctx->slots[slot] && slot_destructors[slot])
110       slot_destructors[slot](ctx->slots[slot]);
111   }
112
113   free(ctx->slots);
114
115   for (i = 0; i < IM_ERROR_COUNT; ++i) {
116     if (ctx->error_stack[i].msg)
117       myfree(ctx->error_stack[i].msg);
118   }
119 #ifdef IMAGER_LOG
120   if (ctx->lg_file)
121     fclose(ctx->lg_file);
122 #endif
123
124   free(ctx);
125 }
126
127 /*
128 =item im_context_clone(ctx)
129
130 Clone an Imager context object, returning the result.
131
132 =cut
133 */
134
135 im_context_t
136 im_context_clone(im_context_t ctx, const char *where) {
137   im_context_t nctx = malloc(sizeof(im_context_struct));
138   int i;
139
140   if (!nctx)
141     return NULL;
142
143   nctx->slot_alloc = slot_count;
144   nctx->slots = calloc(sizeof(void *), ctx->slot_alloc);
145   if (!nctx->slots) {
146     free(nctx);
147     return NULL;
148   }
149
150   nctx->error_sp = ctx->error_sp;
151   for (i = 0; i < IM_ERROR_COUNT; ++i) {
152     if (ctx->error_stack[i].msg) {
153       size_t sz = ctx->error_alloc[i];
154       nctx->error_alloc[i] = sz;
155       nctx->error_stack[i].msg = mymalloc(sz);
156       memcpy(nctx->error_stack[i].msg, ctx->error_stack[i].msg, sz);
157     }
158     else {
159       nctx->error_alloc[i] = 0;
160       nctx->error_stack[i].msg = NULL;
161     }
162     nctx->error_stack[i].code = ctx->error_stack[i].code;
163   }
164 #ifdef IMAGER_LOG
165   nctx->log_level = ctx->log_level;
166   if (ctx->lg_file) {
167     /* disable buffering, this isn't perfect */
168     setvbuf(ctx->lg_file, NULL, _IONBF, 0);
169
170     /* clone that and disable buffering some more */
171     nctx->lg_file = fdopen(fileno(ctx->lg_file), "a");
172     if (nctx->lg_file)
173       setvbuf(nctx->lg_file, NULL, _IONBF, 0);
174   }
175   else {
176     nctx->lg_file = NULL;
177   }
178 #endif
179   nctx->max_width = ctx->max_width;
180   nctx->max_height = ctx->max_height;
181   nctx->max_bytes = ctx->max_bytes;
182
183   nctx->refcount = 1;
184
185 #ifdef IMAGER_TRACE_CONTEXT
186   fprintf(stderr, "im_context:%s: cloned %p to %p\n", where, ctx, nctx);
187 #endif
188
189   return nctx;
190 }
191
192 /*
193 =item im_context_slot_new(destructor, where)
194
195 Allocate a new context-local-storage slot.
196
197 =cut
198 */
199
200 im_slot_t
201 im_context_slot_new(im_slot_destroy_t destructor, const char *where) {
202   im_slot_t new_slot;
203   im_slot_destroy_t *new_destructors;
204   if (!slot_mutex)
205     slot_mutex = i_mutex_new();
206
207   i_mutex_lock(slot_mutex);
208
209   new_slot = slot_count++;
210   new_destructors = realloc(slot_destructors, sizeof(void *) * slot_count);
211   if (!new_destructors)
212     i_fatal(1, "Cannot allocate memory for slot destructors");
213   slot_destructors = new_destructors;
214
215   slot_destructors[new_slot] = destructor;
216
217 #ifdef IMAGER_TRACE_CONTEXT
218   fprintf(stderr, "im_context: slot %d allocated for %s\n",
219           (int)new_slot, where);
220 #endif
221
222   i_mutex_unlock(slot_mutex);
223
224   return new_slot;
225 }
226
227 /*
228 =item im_context_slot_set(slot, value)
229
230 Set the value of a slot.
231
232 Returns true on success.
233
234 Aborts if the slot supplied is invalid.
235
236 If reallocation of slot storage fails, returns false.
237
238 =cut
239 */
240
241 int
242 im_context_slot_set(im_context_t ctx, im_slot_t slot, void *value) {
243   if (slot < 0 || slot >= slot_count) {
244     fprintf(stderr, "Invalid slot %d (valid 0 - %d)\n",
245             (int)slot, (int)slot_count-1);
246     abort();
247   }
248
249   if (slot >= ctx->slot_alloc) {
250     ssize_t i;
251     size_t new_alloc = slot_count;
252     void **new_slots = realloc(ctx->slots, sizeof(void *) * new_alloc);
253
254     if (!new_slots)
255       return 0;
256
257     for (i = ctx->slot_alloc; i < new_alloc; ++i)
258       new_slots[i] = NULL;
259
260     ctx->slots = new_slots;
261     ctx->slot_alloc = new_alloc;
262   }
263
264   ctx->slots[slot] = value;
265
266 #ifdef IMAGER_TRACE_CONTEXT
267   fprintf(stderr, "im_context: ctx %p slot %d set to %p\n",
268           ctx, (int)slot, value);
269 #endif
270
271   return 1;
272 }
273
274 /*
275 =item im_context_slot_get(ctx, slot)
276
277 Retrieve the value previously stored in the given slot of the context
278 object.
279
280 =cut
281 */
282
283 void *
284 im_context_slot_get(im_context_t ctx, im_slot_t slot) {
285   if (slot < 0 || slot >= slot_count) {
286     fprintf(stderr, "Invalid slot %d (valid 0 - %d)\n",
287             (int)slot, (int)slot_count-1);
288     abort();
289   }
290
291   if (slot >= ctx->slot_alloc)
292     return NULL;
293
294 #ifdef IMAGER_TRACE_CONTEXT
295   fprintf(stderr, "im_context: ctx %p slot %d retrieved as %p\n",
296           ctx, (int)slot, ctx->slots[slot]);
297 #endif
298
299   return ctx->slots[slot];
300 }