]> git.imager.perl.org - imager.git/blobdiff - context.c
add new comparison method rgb_difference that resembles arithmetical difference per...
[imager.git] / context.c
index 9967c66b683bc312ed870b863291e7ee8e12b7c6..99f716806fe35d2799b48dfd6d93c7a80675d0f6 100644 (file)
--- a/context.c
+++ b/context.c
@@ -1,4 +1,9 @@
 #include "imageri.h"
+#include <stdio.h>
+
+static volatile im_slot_t slot_count = 1;
+static im_slot_destroy_t *volatile slot_destructors;
+static volatile i_mutex_t slot_mutex;
 
 /*
 =item im_context_new()
@@ -13,6 +18,9 @@ im_context_new(void) {
   im_context_t ctx = malloc(sizeof(im_context_struct));
   int i;
 
+  if (!slot_mutex)
+    slot_mutex = i_mutex_new();
+
   if (!ctx)
     return NULL;
   
@@ -30,27 +38,103 @@ im_context_new(void) {
   ctx->max_height = 0;
   ctx->max_bytes = DEF_BYTES_LIMIT;
 
+  ctx->slot_alloc = slot_count;
+  ctx->slots = calloc(sizeof(void *), ctx->slot_alloc);
+  if (!ctx->slots) {
+    free(ctx);
+    return NULL;
+  }
+
+  ctx->file_magic = NULL;
+
+  ctx->refcount = 1;
+
+#ifdef IMAGER_TRACE_CONTEXT
+  fprintf(stderr, "im_context: created %p\n", ctx);
+#endif
+
+
   return ctx;
 }
 
 /*
-=item im_context_delete(ctx)
+=item im_context_refinc(ctx, where)
+X<im_context_refinc API>
+=section Context objects
+=synopsis im_context_refinc(aIMCTX, "a description");
+
+Add a new reference to the context.
+
+=cut
+*/
+
+void
+im_context_refinc(im_context_t ctx, const char *where) {
+  ++ctx->refcount;
+
+#ifdef IMAGER_TRACE_CONTEXT
+  fprintf(stderr, "im_context:%s: refinc %p (count now %lu)\n", where,
+         ctx, (unsigned long)ctx->refcount);
+#endif
+}
+
+/*
+=item im_context_refdec(ctx, where)
+X<im_context_refdec API>
+=section Context objects
+=synopsis im_context_refdec(aIMCTX, "a description");
 
-Release memory used by an Imager context object.
+Remove a reference to the context, releasing it if all references have
+been removed.
 
 =cut
 */
 
 void
-im_context_delete(im_context_t ctx) {
+im_context_refdec(im_context_t ctx, const char *where) {
   int i;
+  im_slot_t slot;
+
+  im_assert(ctx->refcount > 0);
+
+  --ctx->refcount;
+
+#ifdef IMAGER_TRACE_CONTEXT
+  fprintf(stderr, "im_context:%s: delete %p (count now %lu)\n", where,
+         ctx, (unsigned long)ctx->refcount);
+#endif
+
+  if (ctx->refcount != 0)
+    return;
+
+  /* lock here to avoid slot_destructors from being moved under us */
+  i_mutex_lock(slot_mutex);
+  for (slot = 0; slot < ctx->slot_alloc; ++slot) {
+    if (ctx->slots[slot] && slot_destructors[slot])
+      slot_destructors[slot](ctx->slots[slot]);
+  }
+  i_mutex_unlock(slot_mutex);
+
+  free(ctx->slots);
 
   for (i = 0; i < IM_ERROR_COUNT; ++i) {
     if (ctx->error_stack[i].msg)
       myfree(ctx->error_stack[i].msg);
   }
+
+  {
+    im_file_magic *p = ctx->file_magic;
+    while (p != NULL) {
+      im_file_magic *n = p->next;
+      free(p->m.name);
+      free(p->m.magic);
+      free(p->m.mask);
+      free(p);
+      p = n;
+    }
+  }
 #ifdef IMAGER_LOG
-  if (ctx->lg_file)
+  if (ctx->lg_file && ctx->own_log)
     fclose(ctx->lg_file);
 #endif
 
@@ -62,41 +146,56 @@ im_context_delete(im_context_t ctx) {
 
 Clone an Imager context object, returning the result.
 
+The error stack is not copied from the original context.
+
 =cut
 */
 
 im_context_t
-im_context_clone(im_context_t ctx) {
+im_context_clone(im_context_t ctx, const char *where) {
   im_context_t nctx = malloc(sizeof(im_context_struct));
   int i;
 
   if (!nctx)
     return NULL;
 
-  nctx->error_sp = ctx->error_sp;
+  nctx->slot_alloc = slot_count;
+  nctx->slots = calloc(sizeof(void *), nctx->slot_alloc);
+  if (!nctx->slots) {
+    free(nctx);
+    return NULL;
+  }
+
+  nctx->error_sp = IM_ERROR_COUNT-1;
   for (i = 0; i < IM_ERROR_COUNT; ++i) {
-    if (ctx->error_stack[i].msg) {
-      size_t sz = ctx->error_alloc[i];
-      nctx->error_alloc[i] = sz;
-      nctx->error_stack[i].msg = mymalloc(sz);
-      memcpy(nctx->error_stack[i].msg, ctx->error_stack[i].msg, sz);
-    }
-    else {
-      nctx->error_alloc[i] = 0;
-      nctx->error_stack[i].msg = NULL;
-    }
-    nctx->error_stack[i].code = ctx->error_stack[i].code;
+    nctx->error_alloc[i] = 0;
+    nctx->error_stack[i].msg = NULL;
   }
 #ifdef IMAGER_LOG
   nctx->log_level = ctx->log_level;
   if (ctx->lg_file) {
-    /* disable buffering, this isn't perfect */
-    setvbuf(ctx->lg_file, NULL, _IONBF, 0);
-
-    /* clone that and disable buffering some more */
-    nctx->lg_file = fdopen(fileno(ctx->lg_file), "a");
-    if (nctx->lg_file)
-      setvbuf(nctx->lg_file, NULL, _IONBF, 0);
+    if (ctx->own_log) {
+      int newfd = dup(fileno(ctx->lg_file));
+      if (newfd >= 0) {
+        nctx->own_log = 1;
+        nctx->lg_file = fdopen(newfd, "w");
+        if (nctx->lg_file)
+         setvbuf(nctx->lg_file, NULL, _IONBF, BUFSIZ);
+      }
+      else {
+#ifdef IMAGER_TRACE_CONTEXT
+       perror("im_context:failed to clone log");
+#endif
+       free(nctx->slots);
+       free(nctx);
+       return NULL;
+      }
+    }
+    else {
+      /* stderr */
+      nctx->lg_file = ctx->lg_file;
+      nctx->own_log = 0;
+    }
   }
   else {
     nctx->lg_file = NULL;
@@ -106,5 +205,181 @@ im_context_clone(im_context_t ctx) {
   nctx->max_height = ctx->max_height;
   nctx->max_bytes = ctx->max_bytes;
 
-  return ctx;
+  nctx->refcount = 1;
+
+  {
+    im_file_magic *inp = ctx->file_magic;
+    im_file_magic **outpp = &nctx->file_magic;
+    *outpp = NULL;
+    while (inp) {
+      im_file_magic *m = malloc(sizeof(im_file_magic));
+      if (!m) {
+       /* should free any parts of the list already allocated */
+       im_context_refdec(nctx, "failed cloning");
+       return NULL;
+      }
+      m->next = NULL;
+      m->m.name = strdup(inp->m.name);
+      m->m.magic_size = inp->m.magic_size;
+      m->m.magic = malloc(inp->m.magic_size);
+      m->m.mask = malloc(inp->m.magic_size);
+      if (m->m.name == NULL || m->m.magic == NULL || m->m.mask == NULL) {
+       free(m->m.name);
+       free(m->m.magic);
+       free(m->m.mask);
+       free(m);
+       im_context_refdec(nctx, "failed cloning");
+       return NULL;
+      }
+      memcpy(m->m.magic, inp->m.magic, m->m.magic_size);
+      memcpy(m->m.mask, inp->m.mask, m->m.magic_size);
+      *outpp = m;
+      outpp = &m->next;
+      inp = inp->next;
+    }
+  }
+
+#ifdef IMAGER_TRACE_CONTEXT
+  fprintf(stderr, "im_context:%s: cloned %p to %p\n", where, ctx, nctx);
+#endif
+
+  return nctx;
+}
+
+/*
+=item im_context_slot_new(destructor)
+
+Allocate a new context-local-storage slot.
+
+C<desctructor> will be called when the context is destroyed if the
+corresponding slot is non-NULL.
+
+=cut
+*/
+
+im_slot_t
+im_context_slot_new(im_slot_destroy_t destructor) {
+  im_slot_t new_slot;
+  im_slot_destroy_t *new_destructors;
+  if (!slot_mutex)
+    slot_mutex = i_mutex_new();
+
+  i_mutex_lock(slot_mutex);
+
+  new_slot = slot_count++;
+  new_destructors = realloc(slot_destructors, sizeof(void *) * slot_count);
+  if (!new_destructors)
+    i_fatal(1, "Cannot allocate memory for slot destructors");
+  slot_destructors = new_destructors;
+
+  slot_destructors[new_slot] = destructor;
+
+  i_mutex_unlock(slot_mutex);
+
+  return new_slot;
+}
+
+/*
+=item im_context_slot_set(slot, value)
+
+Set the value of a slot.
+
+Returns true on success.
+
+Aborts if the slot supplied is invalid.
+
+If reallocation of slot storage fails, returns false.
+
+=cut
+*/
+
+int
+im_context_slot_set(im_context_t ctx, im_slot_t slot, void *value) {
+  if (slot < 0 || slot >= slot_count) {
+    fprintf(stderr, "Invalid slot %d (valid 0 - %d)\n",
+           (int)slot, (int)slot_count-1);
+    abort();
+  }
+
+  if (slot >= ctx->slot_alloc) {
+    ssize_t i;
+    size_t new_alloc = slot_count;
+    void **new_slots = realloc(ctx->slots, sizeof(void *) * new_alloc);
+
+    if (!new_slots)
+      return 0;
+
+    for (i = ctx->slot_alloc; i < new_alloc; ++i)
+      new_slots[i] = NULL;
+
+    ctx->slots = new_slots;
+    ctx->slot_alloc = new_alloc;
+  }
+
+  ctx->slots[slot] = value;
+
+  return 1;
+}
+
+/*
+=item im_context_slot_get(ctx, slot)
+
+Retrieve the value previously stored in the given slot of the context
+object.
+
+=cut
+*/
+
+void *
+im_context_slot_get(im_context_t ctx, im_slot_t slot) {
+  if (slot < 0 || slot >= slot_count) {
+    fprintf(stderr, "Invalid slot %d (valid 0 - %d)\n",
+           (int)slot, (int)slot_count-1);
+    abort();
+  }
+
+  if (slot >= ctx->slot_alloc)
+    return NULL;
+
+  return ctx->slots[slot];
+}
+
+/*
+=item im_add_file_magic(ctx, name, bits, mask, length)
+
+Add file type magic to the given context.
+
+=cut
+*/
+
+int
+im_add_file_magic(im_context_t ctx, const char *name,
+                 const unsigned char *bits, const unsigned char *mask,
+                 size_t length) {
+  im_file_magic *m = malloc(sizeof(im_file_magic));
+
+  if (m == NULL)
+    return 0;
+
+  if (length > 512)
+    length = 512;
+
+  m->m.name = strdup(name);
+  m->m.magic = malloc(length);
+  m->m.mask = malloc(length);
+  m->m.magic_size = length;
+
+  if (name == NULL || bits == NULL || mask == NULL) {
+    free(m->m.name);
+    free(m->m.magic);
+    free(m->m.mask);
+    free(m);
+    return 0;
+  }
+  memcpy(m->m.magic, bits, length);
+  memcpy(m->m.mask, mask, length);
+  m->next = ctx->file_magic;
+  ctx->file_magic = m;
+
+  return 1;
 }