00001
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041 #if HAVE_CONFIG_H
00042 #include <config.h>
00043 #endif
00044 #include "logerr.h"
00045 #if HAVE_PTHREADS
00046 #include <pthread.h>
00048 #define POSIX_THREADS 1
00049 #else
00050
00051 #define POSIX_THREADS 0
00052 #endif
00053
00054 #include <unistd.h>
00055 #include <errno.h>
00056 #include <stdlib.h>
00057 #include <string.h>
00058 #include <inttypes.h>
00059 #include <sadie.h>
00060 #include "Sadie_Index.h"
00061 #if WITH_DMALLOC
00062 #include <dmalloc.h>
00063 #endif
00064
00065 static const int maxsize = 14;
00066 static const int minsize = 4;
00067 static const uint32_t fnv32hash_magic_init = 0x811c9dc5;
00069 typedef struct Sad_property * sad_propertyp_t;
00070 typedef struct Sad_docitem * sad_docitemp_t;
00071 typedef struct Sad_doc * sad_docp_t;
00072 typedef struct Sad_propertyitem * sad_propertyitemp_t;
00075 struct Sad_doclist
00076 {
00077 #ifdef HAVE_PTHREADS
00078 pthread_mutex_t lock;
00079 #else
00080 int lock[6];
00081 #endif
00082 uint32_t log2nbuckets;
00083 sad_docitemp_t *buckets;
00084 };
00085
00087 struct Sad_docitem
00088 {
00089 size_t keylen;
00090 const char *key;
00091 sad_docp_t doc;
00092 sad_docitemp_t next;
00093 };
00094
00096 struct Sad_property
00097 {
00098 void *data;
00099 sad_property_data_destructor_t datadestruct;
00100 };
00101
00103 struct Sad_propertyitem
00104 {
00105 size_t keylen;
00106 const char *key;
00107 sad_propertyp_t prop;
00108 sad_propertyitemp_t next;
00109 };
00110
00112 struct Sad_doc
00113 {
00114 uint32_t log2nbuckets;
00115 sad_propertyitemp_t *buckets;
00116 const IMAGE *baseimg;
00117 };
00118
00119 #ifndef HAVE_PTHREADS
00120
00124 static int pthread_mutex_lock (void * dummy) { return 0; }
00125 static int pthread_mutex_unlock (void * dummy) { return 0; }
00126 static int pthread_mutex_destroy (void * dummy) { return 0; }
00127 static int pthread_mutex_init (void * dummy1, void * dummy2) { return 0; }
00129 #endif
00130
00141 static size_t
00142 hash_string (const char *str, const uint32_t log2len, size_t *slen)
00143 {
00144 uint32_t hash;
00145 uint32_t mask;
00146 unsigned char* usp;
00147
00148 logerr_assert (LOGERR_CRIT, LOGERR_FUNC, ((str != NULL) && (slen != NULL)));
00149 logerr_assert (LOGERR_DEBUG, LOGERR_FUNC,
00150 ((minsize <= log2len) && (log2len <= maxsize)));
00151 hash = fnv32hash_magic_init;
00152 usp = (unsigned char *) str;
00153 *slen = 1;
00154 while (*usp)
00155 {
00156 (*slen)++;
00157 hash ^= (uint32_t) *usp++;
00158 hash += ((hash << 1) + (hash << 4) + (hash << 7)
00159 + (hash << 8) + (hash << 24));
00160 }
00161 mask = (1 << log2len) - 1;
00162 return (size_t) (hash & mask);
00163 }
00164
00170 static void
00171 dispose_propertyitem (sad_propertyitemp_t thepropertyitem)
00172 {
00173 logerr_assert (LOGERR_CRIT, LOGERR_FUNC, (thepropertyitem != NULL));
00174 logerr_assert (LOGERR_DEBUG, LOGERR_FUNC,
00175 ((thepropertyitem->key != NULL) == (thepropertyitem->keylen > 0)));
00176 free ((char *) thepropertyitem->key);
00177 if (thepropertyitem->prop != NULL)
00178 {
00179 logerr_assert (LOGERR_CRIT, LOGERR_FUNC,
00180 (thepropertyitem->prop->datadestruct != NULL));
00181 (*(thepropertyitem->prop->datadestruct)) (thepropertyitem->prop->data);
00182 free (thepropertyitem->prop);
00183 }
00184 free (thepropertyitem);
00185 }
00186
00194 static void
00195 dispose_doc (sad_docp_t thedoc)
00196 {
00197 size_t i, nbuckets;
00198 sad_propertyitemp_t *buck;
00199 sad_propertyitemp_t d, v;
00200
00201 logerr_assert (LOGERR_CRIT, LOGERR_FUNC, (thedoc != NULL));
00202 RELIMG ((IMAGE**) &thedoc->baseimg);
00203 nbuckets = (size_t) (1 << thedoc->log2nbuckets);
00204 for (i = nbuckets, buck = thedoc->buckets; i; i--, buck++)
00205 {
00206 d = *buck;
00207 while (d)
00208 {
00209 v = d;
00210 d = d->next;
00211 dispose_propertyitem (v);
00212 }
00213 }
00214 free (thedoc->buckets);
00215 free (thedoc);
00216 }
00217
00222 static void
00223 dispose_docitem (sad_docitemp_t thedocitem)
00224 {
00225 logerr_assert (LOGERR_CRIT, LOGERR_FUNC, (thedocitem != NULL));
00226 logerr_assert (LOGERR_DEBUG, LOGERR_FUNC,
00227 ((thedocitem->key != NULL) == (thedocitem->keylen > 0)));
00228 free ((char *) thedocitem->key);
00229 if (thedocitem->doc != NULL)
00230 dispose_doc (thedocitem->doc);
00231 free (thedocitem);
00232 }
00233
00240 void
00241 dispose_doclist (sad_doclistp_t thedoclist)
00242 {
00243 int err = 0;
00244 size_t i, nbuckets;
00245 sad_docitemp_t *buck;
00246 sad_docitemp_t d, v;
00247
00248 if (thedoclist)
00249 {
00250 logerr_assert (LOGERR_DEBUG, LOGERR_FUNC,
00251 ((thedoclist->buckets != NULL)
00252 && (thedoclist->log2nbuckets >= minsize)
00253 && (thedoclist->log2nbuckets <= maxsize)));
00254 if (POSIX_THREADS)
00255 {
00256 err = pthread_mutex_lock (&thedoclist->lock);
00257 if (err)
00258 {
00259 logerr_err (LOGERR_FUNC,
00260 "could not acquire lock to dispose of doclist: error = %d",
00261 err);
00262 return;
00263 }
00264 }
00265 nbuckets = (size_t) (1 << thedoclist->log2nbuckets);
00266 for (i = nbuckets, buck = thedoclist->buckets; i; i--, buck++)
00267 {
00268 d = *buck;
00269 while (d)
00270 {
00271 v = d;
00272 d = d->next;
00273 dispose_docitem (v);
00274 }
00275 }
00276 free (thedoclist->buckets);
00277 if (POSIX_THREADS)
00278 {
00279 err = pthread_mutex_unlock (&thedoclist->lock);
00280 if (err)
00281 {
00282 logerr_err (LOGERR_FUNC,
00283 "could not release lock to dispose of doclist: error = %d",
00284 err);
00285 return;
00286 }
00287 err = pthread_mutex_destroy (&thedoclist->lock);
00288 if (err)
00289 {
00290 logerr_err (LOGERR_FUNC,
00291 "failed to destroy the doclist lock during disposal: error = %d",
00292 err);
00293 return;
00294 }
00295 }
00296 free (thedoclist);
00297 }
00298 }
00299
00308 static sad_docp_t
00309 make_doc (int logpsize, const IMAGE *img)
00310 {
00311 size_t nbuckets;
00312 sad_docp_t newdoc = NULL;
00313
00314 newdoc = malloc (sizeof (struct Sad_doc));
00315 if (newdoc)
00316 {
00317 if (logpsize < minsize)
00318 logpsize = minsize;
00319 if (logpsize > maxsize)
00320 logpsize = maxsize;
00321 newdoc->log2nbuckets = logpsize;
00322 nbuckets = (size_t) (1 << logpsize);
00323 newdoc->buckets = calloc (nbuckets, sizeof (sad_propertyitemp_t));
00324 if (newdoc->buckets != NULL)
00325 newdoc->baseimg = img;
00326 else
00327 {
00328 free (newdoc);
00329 newdoc = NULL;
00330 }
00331 }
00332 return newdoc;
00333 }
00334
00344 sad_propertyp_t
00345 make_property (sad_property_data_destructor_t yourdestruct, void *yourdata)
00346 {
00347 sad_propertyp_t newproperty;
00348
00349 logerr_assert (LOGERR_CRIT, LOGERR_FUNC,
00350 ((yourdestruct != NULL) && (yourdata != NULL)));
00351 newproperty = malloc (sizeof (struct Sad_property));
00352 if (newproperty)
00353 {
00354 newproperty->data = yourdata;
00355 newproperty->datadestruct = yourdestruct;
00356 }
00357 return newproperty;
00358 }
00359
00368 sad_doclistp_t
00369 make_doclist (int logsize)
00370 {
00371 int err = 0;
00372 size_t nbuckets;
00373 sad_doclistp_t thedoclist;
00374
00375 thedoclist = malloc (sizeof (struct Sad_doclist));
00376 if (thedoclist)
00377 {
00378 if (POSIX_THREADS)
00379 {
00380 err = pthread_mutex_init (&thedoclist->lock, NULL);
00381 if (err)
00382 {
00383 logerr_err (LOGERR_FUNC,
00384 "could not initialize the doclist lock: error = %d",
00385 err);
00386 free (thedoclist);
00387 return NULL;
00388 }
00389 }
00390 if (logsize < minsize)
00391 logsize = minsize;
00392 if (logsize > maxsize)
00393 logsize = maxsize;
00394 thedoclist->log2nbuckets = logsize;
00395 nbuckets = (size_t) (1 << logsize);
00396 thedoclist->buckets = calloc (nbuckets, sizeof (sad_docitemp_t));
00397 if (thedoclist->buckets == NULL)
00398 {
00399 if (POSIX_THREADS)
00400 {
00401 err = pthread_mutex_destroy (&thedoclist->lock);
00402 if (err)
00403 logerr_err (LOGERR_FUNC,
00404 "failed to destroy the doclist lock during bucket allocation failure: error = %d",
00405 err);
00406 }
00407 free (thedoclist);
00408 thedoclist = NULL;
00409 }
00410 }
00411 return thedoclist;
00412 }
00413
00426 static sad_docitemp_t
00427 make_docitem (const size_t dkeylen, const char *dkey, const sad_docp_t docp,
00428 const sad_docitemp_t dinext)
00429 {
00430 sad_docitemp_t newitem;
00431 char *newkey;
00432
00433 logerr_assert (LOGERR_CRIT, LOGERR_FUNC,
00434 ((dkeylen > 0) && (dkey != NULL) && (docp != NULL)));
00435 newitem = malloc (sizeof (struct Sad_docitem));
00436 if (newitem)
00437 {
00438 newkey = calloc (dkeylen, 1);
00439 if ((newkey == NULL) || (strncpy (newkey, dkey, dkeylen) == NULL))
00440 {
00441 free (newitem);
00442 newitem = NULL;
00443 }
00444 else
00445 {
00446 newitem->keylen = dkeylen;
00447 newitem->key = (const char *) newkey;
00448 newitem->doc = docp;
00449 newitem->next = dinext;
00450 }
00451 }
00452 return newitem;
00453 }
00454
00467 static sad_propertyitemp_t
00468 make_propertyitem (const size_t pkeylen, const char *pkey,
00469 const sad_propertyp_t property,
00470 const sad_propertyitemp_t pinext)
00471 {
00472 sad_propertyitemp_t newitem;
00473 char *newkey;
00474
00475 logerr_assert (LOGERR_CRIT, LOGERR_FUNC,
00476 ((pkeylen > 0) && (pkey != NULL) && (property != NULL)));
00477 newitem = malloc (sizeof (struct Sad_propertyitem));
00478 if (newitem)
00479 {
00480 newkey = calloc (pkeylen, 1);
00481 if ((newkey == NULL) || (strncpy (newkey, pkey, pkeylen) == NULL))
00482 {
00483 free (newitem);
00484 newitem = NULL;
00485 }
00486 else
00487 {
00488 newitem->keylen = pkeylen;
00489 newitem->key = (const char *) newkey;
00490 newitem->prop = property;
00491 newitem->next = pinext;
00492 }
00493 }
00494 return newitem;
00495 }
00496
00513 int
00514 sad_put_doc (sad_doclistp_t doclist, const char *dkey,
00515 int logpsize, const IMAGE *img)
00516 {
00517 int err = 0;
00518 int failure = 0;
00519 sad_docp_t newdoc;
00520 size_t dkeylen;
00521 size_t hashval;
00522 sad_docitemp_t *dihandle;
00523 sad_docitemp_t newitem;
00524 int uninserted;
00525 int order;
00526
00527 logerr_assert (LOGERR_CRIT, LOGERR_FUNC,
00528 ((doclist != NULL) && (dkey != NULL)));
00529 logerr_assert (LOGERR_DEBUG, LOGERR_FUNC,
00530 ((minsize <= doclist->log2nbuckets)
00531 && (doclist->log2nbuckets <= maxsize)));
00532 newdoc = make_doc (logpsize, img);
00533 logerr_assert (LOGERR_CRIT, LOGERR_FUNC, (newdoc != NULL));
00534 if (POSIX_THREADS)
00535 {
00536 err = pthread_mutex_lock (&doclist->lock);
00537 if (err)
00538 {
00539 logerr_err (LOGERR_FUNC,
00540 "could not acquire lock to insert doc %s: error = %d",
00541 dkey, err);
00542 return err;
00543 }
00544 }
00545 uninserted = 1;
00546 hashval = hash_string (dkey, doclist->log2nbuckets, &dkeylen);
00547 dihandle = &doclist->buckets[hashval];
00548 while ((*dihandle != NULL) && uninserted)
00549 {
00550 order = strcmp (dkey, (*dihandle)->key);
00551 if (order > 0)
00552 dihandle = &(*dihandle)->next;
00553 else
00554 {
00555 if (order < 0)
00556 newitem = make_docitem (dkeylen, dkey, newdoc, *dihandle);
00557 else
00558 {
00559 newitem = make_docitem (dkeylen, dkey, newdoc,
00560 (*dihandle)->next);
00561 dispose_docitem (*dihandle);
00562 }
00563 failure = (newitem == NULL);
00564 uninserted = 0;
00565 *dihandle = newitem;
00566 dihandle = &newitem->next;
00567 }
00568 }
00569 if (uninserted)
00570 {
00571 logerr_assert (LOGERR_DEBUG, LOGERR_FUNC, (*dihandle == NULL));
00572 *dihandle = make_docitem (dkeylen, dkey, newdoc, NULL);
00573 }
00574 if (POSIX_THREADS)
00575 {
00576 err = pthread_mutex_unlock (&doclist->lock);
00577 if (err)
00578 logerr_err (LOGERR_FUNC,
00579 "could not release lock to insert doc %s: error = %d",
00580 dkey, err);
00581 }
00582 return (failure) ? failure : err;
00583 }
00584
00595 int
00596 sad_dispose_doc (sad_doclistp_t doclist, const char *dkey)
00597 {
00598 int err = 0;
00599 size_t dkeylen;
00600 size_t hashval;
00601 sad_docitemp_t *dihandle;
00602 sad_docitemp_t tail;
00603 int order;
00604
00605 logerr_assert (LOGERR_CRIT, LOGERR_FUNC,
00606 ((doclist != NULL) && (dkey != NULL)));
00607 logerr_assert (LOGERR_DEBUG, LOGERR_FUNC,
00608 ((minsize <= doclist->log2nbuckets)
00609 && (doclist->log2nbuckets <= maxsize)));
00610 if (POSIX_THREADS)
00611 {
00612 err = pthread_mutex_lock (&doclist->lock);
00613 if (err)
00614 {
00615 logerrno (LOGERR_ERR, err, LOGERR_FUNC,
00616 "could not acquire the lock to delete doc %s",
00617 dkey);
00618 return err;
00619 }
00620 }
00621 order = 1;
00622 hashval = hash_string (dkey, doclist->log2nbuckets, &dkeylen);
00623 dihandle = &doclist->buckets[hashval];
00624 while ((*dihandle != NULL) && (order > 0))
00625 {
00626 order = strcmp (dkey, (*dihandle)->key);
00627 if (order > 0)
00628 dihandle = &(*dihandle)->next;
00629 else if (order == 0)
00630 {
00631 tail = (*dihandle)->next;
00632 dispose_docitem (*dihandle);
00633 *dihandle = tail;
00634 }
00635 }
00636 if (order > 0)
00637 logerr_warning (LOGERR_FUNC,
00638 "could not find the item to be deleted: doc=%s",
00639 dkey);
00640 if (POSIX_THREADS)
00641 {
00642 err = pthread_mutex_unlock (&doclist->lock);
00643 if (err)
00644 logerrno (LOGERR_ERR, err, LOGERR_FUNC,
00645 "could not release the lock after deleting doc %s",
00646 dkey);
00647 }
00648 return (order > 0) ? -1 : err;
00649 }
00650
00664 static int
00665 put_docitem_property (sad_docitemp_t docitem,
00666 const char *pkey, const sad_propertyp_t prop)
00667 {
00668 int err = 0;
00669 size_t pkeylen;
00670 size_t hashval;
00671 sad_propertyitemp_t *pihandle;
00672 sad_propertyitemp_t newitem;
00673 int order;
00674 int uninserted = 1;
00675
00676 logerr_assert (LOGERR_CRIT, LOGERR_FUNC,
00677 ((docitem != NULL) && (pkey != NULL) && (prop != NULL)));
00678 logerr_assert (LOGERR_DEBUG, LOGERR_FUNC,
00679 ((minsize <= docitem->doc->log2nbuckets)
00680 && (docitem->doc->log2nbuckets <= maxsize)));
00681 hashval = hash_string (pkey, docitem->doc->log2nbuckets, &pkeylen);
00682 pihandle = &docitem->doc->buckets[hashval];
00683 while ((*pihandle != NULL) && uninserted)
00684 {
00685 order = strcmp (pkey, (*pihandle)->key);
00686 if (order > 0)
00687 pihandle = &(*pihandle)->next;
00688 else
00689 {
00690 if (order < 0)
00691 newitem = make_propertyitem (pkeylen, pkey, prop, *pihandle);
00692 else
00693 {
00694 newitem = make_propertyitem (pkeylen, pkey, prop, (*pihandle)->next);
00695 if (newitem)
00696 dispose_propertyitem (*pihandle);
00697 }
00698 if (newitem == NULL)
00699 return -1;
00700 uninserted = 0;
00701 *pihandle = newitem;
00702 pihandle = &newitem->next;
00703 }
00704 }
00705 if (uninserted)
00706 {
00707 logerr_assert (LOGERR_DEBUG, LOGERR_FUNC, (*pihandle == NULL));
00708 *pihandle = make_propertyitem (pkeylen, pkey, prop, NULL);
00709 err = (*pihandle == NULL);
00710 }
00711 return err;
00712 }
00713
00722 static sad_propertyp_t
00723 get_docitem_property (sad_docitemp_t docitem, const char *pkey)
00724 {
00725 size_t pkeylen;
00726 size_t hashval;
00727 sad_propertyitemp_t pip;
00728 sad_propertyp_t theprop = NULL;
00729 int compare;
00730
00731 logerr_assert (LOGERR_CRIT, LOGERR_FUNC, ((docitem != NULL) && (pkey != NULL)));
00732 logerr_assert (LOGERR_DEBUG, LOGERR_FUNC,
00733 ((minsize <= docitem->doc->log2nbuckets)
00734 && (docitem->doc->log2nbuckets <= maxsize)));
00735 hashval = hash_string (pkey, docitem->doc->log2nbuckets, &pkeylen);
00736 pip = docitem->doc->buckets[hashval];
00737 compare = -1;
00738 while ((pip != NULL) && (compare = strcmp (pkey, pip->key)) != 0)
00739 pip = pip->next;
00740 if (pip != NULL)
00741 theprop = pip->prop;
00742 return theprop;
00743 }
00744
00764 int
00765 sad_put_doc_property (sad_doclistp_t doclist, const char *dkey,
00766 const char *pkey,
00767 sad_property_data_destructor_t destruct,
00768 void *data)
00769 {
00770 int err = 0;
00771 int failure = 0;
00772 size_t hashval;
00773 size_t dkeylen;
00774 sad_docitemp_t dip;
00775 sad_propertyp_t prop;
00776 int compare;
00777
00778 logerr_assert (LOGERR_CRIT, LOGERR_FUNC,
00779 ((doclist != NULL) && (dkey != NULL)
00780 && (pkey != NULL)));
00781 logerr_assert (LOGERR_DEBUG, LOGERR_FUNC,
00782 ((minsize <= doclist->log2nbuckets)
00783 && (doclist->log2nbuckets <= maxsize)));
00784 prop = make_property (destruct, data);
00785 if (prop == NULL)
00786 {
00787 logerr_err (LOGERR_FUNC,
00788 "could not create a property: doc = %s, prop = %s",
00789 dkey, pkey);
00790 return -1;
00791 }
00792 if (POSIX_THREADS)
00793 {
00794 err = pthread_mutex_lock (&doclist->lock);
00795 if (err)
00796 {
00797 logerr_err (LOGERR_FUNC,
00798 "could not acquire lock to set a property: doc = %s, prop = %s, error = %d",
00799 dkey, pkey, err);
00800 return err;
00801 }
00802 }
00803 hashval = hash_string (dkey, doclist->log2nbuckets, &dkeylen);
00804 dip = doclist->buckets[hashval];
00805 compare = -1;
00806 while ((dip != NULL) && (compare = strcmp (dkey, dip->key)) != 0)
00807 dip = dip->next;
00808 if (dip != NULL)
00809 failure = put_docitem_property (dip, pkey, prop);
00810 else
00811 {
00812 logerr_err (LOGERR_FUNC,
00813 "could not find the document to set a property: doc = %s, prop = %s, hv = %d",
00814 dkey, pkey, hashval);
00815 failure = -1;
00816 }
00817 if (POSIX_THREADS)
00818 {
00819 err = pthread_mutex_unlock (&doclist->lock);
00820 if (err)
00821 logerr_err (LOGERR_FUNC,
00822 "could not release lock to set a property: doc = %s, prop = %s, error = %d",
00823 dkey, pkey, err);
00824 }
00825 return (failure) ? failure : err;
00826 }
00827
00841 void *
00842 sad_get_doc_property (const sad_doclistp_t doclist, const char *dkey,
00843 const char *pkey)
00844 {
00845 int err = 0;
00846 sad_propertyp_t theprop = NULL;
00847 size_t hashval;
00848 size_t dkeylen;
00849 sad_docitemp_t dip;
00850 int compare;
00851
00852 logerr_assert (LOGERR_CRIT, LOGERR_FUNC,
00853 ((doclist != NULL) && (dkey != NULL) && (pkey != NULL)));
00854 logerr_assert (LOGERR_DEBUG, LOGERR_FUNC,
00855 ((minsize <= doclist->log2nbuckets)
00856 && (doclist->log2nbuckets <= maxsize)));
00857 if (POSIX_THREADS)
00858 {
00859 err = pthread_mutex_lock (&doclist->lock);
00860 if (err)
00861 {
00862 logerr_err (LOGERR_FUNC,
00863 "could not acquire lock to get a property: doc = %s, prop = %s, error = %d",
00864 dkey, pkey, err);
00865 return NULL;
00866 }
00867 }
00868 hashval = hash_string (dkey, doclist->log2nbuckets, &dkeylen);
00869 dip = doclist->buckets[hashval];
00870 compare = -1;
00871 while ((dip != NULL) && ((compare = strcmp (dkey, dip->key)) != 0))
00872 dip = dip->next;
00873 if (dip != NULL)
00874 theprop = get_docitem_property (dip, pkey);
00875 else
00876 logerr_err (LOGERR_FUNC,
00877 "could not find the document to get a property: doc = %s, prop = %s, hv = %d",
00878 dkey, pkey, hashval);
00879 if (POSIX_THREADS)
00880 {
00881 err = pthread_mutex_unlock (&doclist->lock);
00882 if (err)
00883 logerr_err (LOGERR_FUNC,
00884 "could not release lock to get a property: doc = %s, prop = %s, error = %d",
00885 dkey, pkey, err);
00886 }
00887 return (err || (theprop == NULL)) ? NULL : theprop->data;
00888 }
00889
00890