1 /* @source enscache ***********************************************************
2 **
3 ** Ensembl Cache functions
4 **
5 ** @author Copyright (C) 1999 Ensembl Developers
6 ** @author Copyright (C) 2006 Michael K. Schuster
7 ** @version $Revision: 1.40 $
8 ** @modified 2009 by Alan Bleasby for incorporation into EMBOSS core
9 ** @modified $Date: 2013/02/17 13:02:40 $ by $Author: mks $
10 ** @@
11 **
12 ** This library is free software; you can redistribute it and/or
13 ** modify it under the terms of the GNU Lesser General Public
14 ** License as published by the Free Software Foundation; either
15 ** version 2.1 of the License, or (at your option) any later version.
16 **
17 ** This library is distributed in the hope that it will be useful,
18 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
19 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20 ** Lesser General Public License for more details.
21 **
22 ** You should have received a copy of the GNU Lesser General Public
23 ** License along with this library; if not, write to the Free Software
24 ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 ** MA  02110-1301,  USA.
26 **
27 ******************************************************************************/
28 
29 /* ========================================================================= */
30 /* ============================= include files ============================= */
31 /* ========================================================================= */
32 
33 #include "enscache.h"
34 #include "enstable.h"
35 
36 
37 
38 
39 /* ========================================================================= */
40 /* =============================== constants =============================== */
41 /* ========================================================================= */
42 
43 
44 
45 
46 /* ========================================================================= */
47 /* =========================== global variables ============================ */
48 /* ========================================================================= */
49 
50 
51 
52 
53 /* ========================================================================= */
54 /* ============================= private data ============================== */
55 /* ========================================================================= */
56 
57 /* @datastatic CachePNode *****************************************************
58 **
59 ** Ensembl Cache Node.
60 **
61 ** @alias CacheSNode
62 ** @alias CacheONode
63 **
64 ** @attr Key [void*] Key data address
65 ** @attr Value [void*] Value data address
66 ** @attr Bytes [size_t] Byte size of this node including key and value data
67 ** @attr Dirty [AjBool] Flag to mark that value data has not been written back
68 ** @attr Padding [ajuint] Padding to alignment boundary
69 ** @@
70 ******************************************************************************/
71 
72 typedef struct CacheSNode
73 {
74     void *Key;
75     void *Value;
76     size_t Bytes;
77     AjBool Dirty;
78     ajuint Padding;
79 } CacheONode;
80 
81 #define CachePNode CacheONode*
82 
83 
84 
85 
86 /* ========================================================================= */
87 /* =========================== private constants =========================== */
88 /* ========================================================================= */
89 
90 
91 
92 
93 /* ========================================================================= */
94 /* =========================== private variables =========================== */
95 /* ========================================================================= */
96 
97 
98 
99 
100 /* ========================================================================= */
101 /* =========================== private functions =========================== */
102 /* ========================================================================= */
103 
104 static CachePNode cacheNodeNew(const EnsPCache cache, void *key, void *value);
105 
106 static void cacheNodeDel(const EnsPCache cache, CachePNode *Pnode);
107 
108 static AjBool cacheNodeInsert(EnsPCache cache, CachePNode node);
109 
110 static AjBool cacheNodeRemove(EnsPCache cache, const CachePNode node);
111 
112 
113 
114 
115 /* ========================================================================= */
116 /* ======================= All functions by section ======================== */
117 /* ========================================================================= */
118 
119 
120 
121 
122 /* @filesection enscache ******************************************************
123 **
124 ** @nam1rule ens Function belongs to the Ensembl library
125 **
126 ******************************************************************************/
127 
128 
129 
130 
131 /* @funcstatic cacheNodeNew ***************************************************
132 **
133 ** Default constructor for an Ensembl Cache Node.
134 **
135 ** The size of the Cache Node will be estimated according to the Ensembl Cache
136 ** type with the function already provided at the Cache initialisation stage.
137 ** This fuction will also reference value data to increment an internal usage
138 ** counter and prevent deletion of value data while in the cache.
139 **
140 ** @param [r] cache [const EnsPCache] Ensembl Cache
141 ** @param [r] key [void*] Key data address
142 ** @param [r] value [void*] Value data address
143 **
144 ** @return [CachePNode] Ensembl Cache Node or NULL
145 **
146 ** @release 6.2.0
147 ** @@
148 ******************************************************************************/
149 
cacheNodeNew(const EnsPCache cache,void * key,void * value)150 static CachePNode cacheNodeNew(const EnsPCache cache, void *key, void *value)
151 {
152     ajuint *Puintkey = NULL;
153 
154     CachePNode node = NULL;
155 
156     if (!cache)
157         return NULL;
158 
159     if (!key)
160         return NULL;
161 
162     if (!value)
163         return NULL;
164 
165     AJNEW0(node);
166 
167     /* Add the size of the Ensembl Cache Node itself. */
168 
169     node->Bytes = sizeof (CacheONode);
170 
171     switch (cache->Type)
172     {
173         case ensECacheTypeNumeric:
174 
175             /* Copy AJAX unsigned integer key data. */
176 
177             AJNEW0(Puintkey);
178 
179             *Puintkey = *((ajuint *) key);
180 
181             node->Key = (void *) Puintkey;
182 
183             /* Add the size of unsigned integer key data. */
184 
185             node->Bytes += sizeof (ajuint);
186 
187             break;
188 
189         case ensECacheTypeAlphaNumeric:
190 
191             /* Copy AJAX String key data. */
192 
193             node->Key = (void *) ajStrNewS((AjPStr) key);
194 
195             /* Add the size of AJAX String key data. */
196 
197             node->Bytes += sizeof (AjOStr);
198 
199             node->Bytes += ajStrGetRes((AjPStr) node->Key);
200 
201             break;
202 
203         default:
204 
205             ajWarn("cacheNodeNew got unexpected Cache type %d.\n",
206                    cache->Type);
207     }
208 
209     /* Reference the value data. */
210 
211     if (cache->Freference && value)
212         node->Value = (*cache->Freference) (value);
213 
214     /* Calculate the size of the value data. */
215 
216     if (cache->Fsize && node->Value)
217         node->Bytes += (*cache->Fsize) (node->Value);
218 
219     node->Dirty = ajFalse;
220 
221     return node;
222 }
223 
224 
225 
226 
227 /* @funcstatic cacheNodeDel ***************************************************
228 **
229 ** Default destructor for an Ensembl Cache Node.
230 **
231 ** @param [r] cache [const EnsPCache] Ensembl Cache
232 ** @param [d] Pnode [CachePNode*] Ensembl Cache Node address
233 **
234 ** @return [void]
235 **
236 ** @release 6.2.0
237 ** @@
238 ******************************************************************************/
239 
cacheNodeDel(const EnsPCache cache,CachePNode * Pnode)240 static void cacheNodeDel(const EnsPCache cache, CachePNode *Pnode)
241 {
242     CachePNode pthis = NULL;
243 
244     if (!cache)
245         return;
246 
247     if (!Pnode)
248         return;
249 
250 #if defined(AJ_DEBUG) && AJ_DEBUG >= 1
251     if (ajDebugTest("cacheNodeDel"))
252     {
253         ajDebug("cacheNodeDel\n"
254                 "  *Pnode %p\n",
255                 *Pnode);
256 
257         /* cacheNodeTrace(*Pnode, 1); */
258     }
259 #endif /* defined(AJ_DEBUG) && AJ_DEBUG >= 1 */
260 
261     if (!(pthis = *Pnode))
262         return;
263 
264     /* Delete key data. */
265 
266     switch (cache->Type)
267     {
268         case ensECacheTypeNumeric:
269 
270             /* Delete AJAX unsigned integer key data. */
271 
272             AJFREE(pthis->Key);
273 
274             break;
275 
276         case ensECacheTypeAlphaNumeric:
277 
278             /* Delete AJAX String key data. */
279 
280             ajStrDel((AjPStr *) &pthis->Key);
281 
282             break;
283 
284         default:
285 
286             ajWarn("cacheNodeDel got unexpected Cache type %d.\n",
287                    cache->Type);
288     }
289 
290     /* Delete value data. */
291 
292     if (cache->Fdelete && pthis->Value)
293         (*cache->Fdelete) (&pthis->Value);
294 
295     ajMemFree((void **) Pnode);
296 
297     return;
298 }
299 
300 
301 
302 
303 /* @funcstatic cacheNodeInsert ************************************************
304 **
305 ** Insert an Ensembl Cache Node into an Ensembl Cache.
306 **
307 ** @param [u] cache [EnsPCache] Ensembl Cache
308 ** @param [u] node [CachePNode] Ensembl Cache Node
309 **
310 ** @return [AjBool] ajTrue upon success, ajFalse otherwise
311 **
312 ** @release 6.2.0
313 ** @@
314 ******************************************************************************/
315 
cacheNodeInsert(EnsPCache cache,CachePNode node)316 static AjBool cacheNodeInsert(EnsPCache cache, CachePNode node)
317 {
318     CachePNode old = NULL;
319 
320     if (!cache)
321         return ajFalse;
322 
323     if (!node)
324         return ajFalse;
325 
326     if (cache->MaxSize && (node->Bytes > cache->MaxSize))
327         return ajFalse;
328 
329     /* Insert the node into the AJAX List. */
330 
331     ajListPushAppend(cache->List, (void *) node);
332 
333     /* Insert the node into the AJAX Table. */
334 
335     ajTablePut(cache->Table, node->Key, (void *) node);
336 
337     /* Update the cache statistics. */
338 
339     cache->Bytes += node->Bytes;
340 
341     cache->Count++;
342 
343     cache->Stored++;
344 
345     /* If the cache is too big, remove the top node(s). */
346 
347     while ((cache->MaxBytes && (cache->Bytes > cache->MaxBytes)) ||
348            (cache->MaxCount && (cache->Count > cache->MaxCount)))
349     {
350         /* Remove the top node from the AJAX List. */
351 
352         ajListPop(cache->List, (void **) &old);
353 
354         /* Remove the node also from the AJAX Table. */
355 
356         ajTableRemove(cache->Table, old->Key);
357 
358         /* Update the cache statistics. */
359 
360         cache->Bytes -= old->Bytes;
361 
362         cache->Count--;
363 
364         cache->Dropped++;
365 
366         /* Write changes of value data to disk if any. */
367 
368         if (cache->Fwrite && old->Value && old->Dirty)
369             (*cache->Fwrite) (old->Value);
370 
371         /* Both, key and value data are deleted via cacheNodeDel. */
372 
373         cacheNodeDel(cache, &old);
374     }
375 
376     return ajTrue;
377 }
378 
379 
380 
381 
382 /* @funcstatic cacheNodeRemove ************************************************
383 **
384 ** Remove an Ensembl Cache Node from an Ensembl Cache.
385 **
386 ** @param [u] cache [EnsPCache] Ensembl Cache
387 ** @param [r] node [const CachePNode] Ensembl Cache Node
388 **
389 ** @return [AjBool] ajTrue upon success, ajFalse otherwise
390 **
391 ** @release 6.2.0
392 ** @@
393 ******************************************************************************/
394 
cacheNodeRemove(EnsPCache cache,const CachePNode node)395 static AjBool cacheNodeRemove(EnsPCache cache, const CachePNode node)
396 {
397     AjIList iter = NULL;
398 
399     CachePNode lnode = NULL;
400 
401     if (!cache)
402         return ajFalse;
403 
404     if (!node)
405         return ajFalse;
406 
407     /* Remove the node from the AJAX List. */
408 
409     iter = ajListIterNew(cache->List);
410 
411     while (!ajListIterDone(iter))
412     {
413         lnode = (CachePNode) ajListIterGet(iter);
414 
415         if (lnode == node)
416         {
417             ajListIterRemove(iter);
418 
419             break;
420         }
421     }
422 
423     ajListIterDel(&iter);
424 
425     /* Remove the node from the AJAX Table. */
426 
427     ajTableRemove(cache->Table, node->Key);
428 
429     /* Update the cache statistics. */
430 
431     cache->Bytes -= node->Bytes;
432 
433     cache->Count--;
434 
435     cache->Removed++;
436 
437     return ajTrue;
438 }
439 
440 
441 
442 
443 /* @datasection [EnsPCache] Ensembl Cache *************************************
444 **
445 ** @nam2rule Cache Functions for manipulating Ensembl Cache objects
446 ** @cc Bio::EnsEMBL::Utils::Cache
447 ** @cc CVS Revision: 1.3
448 ** @cc CVS Tag: branch-ensembl-68
449 **
450 ******************************************************************************/
451 
452 
453 
454 
455 /* @section constructors ******************************************************
456 **
457 ** @fdata [EnsPCache]
458 **
459 ** @nam3rule New Constructor
460 **
461 ** @argrule New type [const EnsECacheType] Ensembl Cache type
462 ** @argrule New maxbytes [size_t] Maximum number of bytes held in the cache
463 ** @argrule New maxcount [ajuint] Maximum number of objects to be cached
464 ** @argrule New maxsize [size_t] Maximum size of an object to be cached
465 ** @argrule New Freference [void* function] Object-specific referencing
466 ** function
467 ** @argrule New Fdelete [void function] Object-specific deletion function
468 ** @argrule New Fsize [size_t function] Object-specific memory sizing function
469 ** @argrule New Fread [void* function] Object-specific reading function
470 ** @argrule New Fwrite [AjBool function] Object-specific writing function
471 ** @argrule New synchron [AjBool] ajTrue: Immediately write-back value data
472 ** @argrule New label [const char*] Cache label for statistics output
473 **
474 ** @valrule * [EnsPCache] Ensembl Cache or NULL
475 **
476 ** @fcategory new
477 ******************************************************************************/
478 
479 
480 
481 
482 /* @func ensCacheNew **********************************************************
483 **
484 ** Default constructor for an Ensembl Cache.
485 **
486 ** @param [r] type [const EnsECacheType] Ensembl Cache type
487 **                 (ensECacheTypeNumeric or ensECacheTypeAlphaNumeric)
488 ** @param [r] maxbytes [size_t] Maximum number of bytes held in the cache
489 ** @param [r] maxcount [ajuint] Maximum number of objects to be cached
490 ** @param [r] maxsize [size_t] Maximum size of an object to be cached
491 ** @param [f] Freference [void* function] Object-specific referencing function
492 ** @param [f] Fdelete [void function] Object-specific deletion function
493 ** @param [f] Fsize [size_t function] Object-specific memory sizing function
494 ** @param [f] Fread [void* function] Object-specific reading function
495 ** @param [f] Fwrite [AjBool function] Object-specific writing function
496 ** @param [r] synchron [AjBool] ajTrue: Immediately write-back value data
497 **                              ajFalse: Write-back value data later
498 ** @param [r] label [const char*] Cache label for statistics output
499 **
500 ** @return [EnsPCache] Ensembl Cache or NULL
501 **
502 ** @release 6.2.0
503 ** @@
504 ** The maximum size parameter should prevent the cache from purging too many
505 ** objects when very large objects are inserted. If not set it defaults to
506 ** a tenth of the maximum cache size.
507 **
508 ** Object-specific functions are required to reference objects held in the
509 ** cache or delete objects once purged from the cache, as well as memory sizing
510 ** functions and object-specific read and write back functions.
511 ******************************************************************************/
512 
ensCacheNew(const EnsECacheType type,size_t maxbytes,ajuint maxcount,size_t maxsize,void * (* Freference)(void * value),void (* Fdelete)(void ** Pvalue),size_t (* Fsize)(const void * value),void * (* Fread)(const void * key),AjBool (* Fwrite)(const void * value),AjBool synchron,const char * label)513 EnsPCache ensCacheNew(const EnsECacheType type,
514                       size_t maxbytes,
515                       ajuint maxcount,
516                       size_t maxsize,
517                       void* (*Freference) (void *value),
518                       void (*Fdelete) (void **Pvalue),
519                       size_t (*Fsize) (const void *value),
520                       void* (*Fread) (const void *key),
521                       AjBool (*Fwrite) (const void *value),
522                       AjBool synchron,
523                       const char *label)
524 {
525     AjBool debug = AJFALSE;
526 
527     EnsPCache cache = NULL;
528 
529     debug = ajDebugTest("ensCacheNew");
530 
531     if (debug)
532         ajDebug("ensCacheNew\n"
533                 "  type %d\n"
534                 "  maxbytes %Lu\n"
535                 "  maxcount %u\n"
536                 "  maxsize %Lu\n"
537                 "  Freference %p\n"
538                 "  Fdelete %p\n"
539                 "  Fsize %p\n"
540                 "  Fread %p\n"
541                 "  Fwrite %p\n"
542                 "  synchron '%B'\n"
543                 "  label '%s'\n",
544                 type,
545                 maxbytes,
546                 maxcount,
547                 maxsize,
548                 Freference,
549                 Fdelete,
550                 Fsize,
551                 Fread,
552                 Fwrite,
553                 synchron,
554                 label);
555     /* FIXME: size_t can be shorter than ajulong */
556 
557     if ((type < ensECacheTypeNumeric) || (type > ensECacheTypeAlphaNumeric))
558         ajFatal("ensCacheNew requires a valid type.\n");
559 
560     if ((!maxbytes) && (!maxcount))
561         ajFatal("ensCacheNew requires either a "
562                 "maximum bytes or maximum count limit.\n");
563 
564     if (!maxsize)
565         maxsize = maxbytes ? maxbytes / 10 + 1 : 0;
566 
567     if (maxbytes && (!maxsize))
568         ajFatal("ensCacheNew requires a maximum size limit, "
569                 "when a maximum bytes limit is set.");
570 
571     /* TODO: Find and set a sensible value here! */
572     /* FIXME: size_t can be shorter than ajulong */
573     if (debug)
574         ajDebug("ensCacheNew maxbytes %Lu, maxcount %u, maxsize %Lu.\n",
575                 maxbytes, maxcount, maxsize);
576 
577     if (maxbytes && (maxbytes < 1000))
578         ajFatal("ensCacheNew cannot set a maximum bytes limit (%Lu) under "
579                 "1000, as each Cache Node requires %Lu bytes alone.",
580                 maxbytes, sizeof (CacheONode));
581 
582     /* TODO: Find and set a sensible value here! */
583 
584     if (maxsize && (maxsize < 3))
585         ajFatal("ensCacheNew cannot set a maximum size limit (%Lu) under "
586                 "3 bytes. maximum bytes %Lu maximum count %u.",
587                 maxsize, maxbytes, maxcount);
588 
589     /*
590     ** Pointers to functions for automatic reading of data not yet in the
591     ** cache and writing of data modified in cache are not mandatory.
592     ** If not specified the cache will simply lack this functionality.
593     ** However, the specification of a function deleting stale cache entries
594     ** and a function calculating the size of value data are required.
595     */
596 
597     if (!Freference)
598         ajFatal("ensCacheNew requires a referencing function.");
599 
600     if (!Fdelete)
601         ajFatal("ensCacheNew requires a deletion function.");
602 
603     if (maxsize && (!Fsize))
604         ajFatal("ensCacheNew requires a memory sizing function "
605                 "when a maximum size limit has been defined.");
606 
607     if (!label)
608         ajFatal("ensCacheNew requires a label.");
609 
610     AJNEW0(cache);
611 
612     cache->Label = ajStrNewC(label);
613     cache->List  = ajListNew();
614 
615     switch (type)
616     {
617         case ensECacheTypeNumeric:
618 
619             cache->Table = ajTableuintNew(0U);
620 
621             break;
622 
623         case ensECacheTypeAlphaNumeric:
624 
625             cache->Table = ajTablestrNew(0U);
626 
627             break;
628 
629         default:
630 
631             ajWarn("ensCacheNew got unexpected Cache type %d.\n",
632                    cache->Type);
633     }
634 
635     /*
636     ** Since the AJAX Table does not use real key or value data,
637     ** both, keydel and valdel need setting to NULL.
638     */
639 
640     ajTableSetDestroy(
641         cache->Table,
642         (void (*)(void **)) NULL,
643         (void (*)(void **)) NULL);
644 
645     cache->Freference = Freference;
646     cache->Fdelete    = Fdelete;
647     cache->Fsize      = Fsize;
648     cache->Fread      = Fread;
649     cache->Fwrite     = Fwrite;
650     cache->Type       = type;
651     cache->Synchron   = synchron;
652     cache->MaxBytes   = maxbytes;
653     cache->MaxCount   = maxcount;
654     cache->MaxSize    = maxsize;
655     cache->Bytes      = 0;
656     cache->Count      = 0;
657     cache->Dropped    = 0;
658     cache->Removed    = 0;
659     cache->Stored     = 0;
660     cache->Hit        = 0;
661     cache->Miss       = 0;
662 
663     return cache;
664 }
665 
666 
667 
668 
669 /* @section destructors *******************************************************
670 **
671 ** Destruction destroys all internal data structures and frees the memory
672 ** allocated for an Ensembl Cache object.
673 **
674 ** @fdata [EnsPCache]
675 **
676 ** @nam3rule Del Destroy (free) an Ensembl Cache
677 **
678 ** @argrule * Pcache [EnsPCache*] Ensembl Cache address
679 **
680 ** @valrule * [void]
681 **
682 ** @fcategory delete
683 ******************************************************************************/
684 
685 
686 
687 
688 /* @func ensCacheDel **********************************************************
689 **
690 ** Default destructor for an Ensembl Cache.
691 **
692 ** @param [u] Pcache [EnsPCache*] Ensembl Cache address
693 **
694 ** @return [void]
695 **
696 ** @release 6.2.0
697 ** @@
698 ** Value data in Cache Nodes that have not been synchronised are written-back.
699 ** Cache flags are reset for value data before the value data is deleted.
700 ** After deletion of all Cache Nodes a summary statistics is printed and the
701 ** Ensembl Cache is destroyed.
702 ******************************************************************************/
703 
ensCacheDel(EnsPCache * Pcache)704 void ensCacheDel(EnsPCache *Pcache)
705 {
706     AjBool debug = AJFALSE;
707 
708     EnsPCache pthis = NULL;
709 
710     if (!Pcache)
711         return;
712 
713 #if defined(AJ_DEBUG) && AJ_DEBUG >= 1
714     debug = ajDebugTest("ensCacheDel");
715 
716     if (debug)
717         ajDebug("ensCacheDel\n"
718                 "  *Pcache %p\n",
719                 *Pcache);
720 #endif /* defined(AJ_DEBUG) && AJ_DEBUG >= 1 */
721 
722     if (!(pthis = *Pcache))
723         return;
724 
725     ensCacheClear(pthis);
726 
727     if (debug)
728         ensCacheTrace(pthis, 1);
729 
730     ajStrDel(&pthis->Label);
731 
732     ajListFree(&pthis->List);
733 
734     ajTableFree(&pthis->Table);
735 
736     ajMemFree((void **) Pcache);
737 
738     return;
739 }
740 
741 
742 
743 
744 /* @section clear *************************************************************
745 **
746 ** Clear an Ensembl Cache.
747 **
748 ** @fdata [EnsPCache]
749 **
750 ** @nam3rule Clear Clear an Ensembl Cache
751 **
752 ** @argrule * cache [EnsPCache] Ensembl Cache
753 **
754 ** @valrule * [AjBool] ajTrue upon success, ajFalse otherwise
755 **
756 ** @fcategory modify
757 ******************************************************************************/
758 
759 
760 
761 
762 /* @func ensCacheClear ********************************************************
763 **
764 ** Clear an Ensembl Cache.
765 **
766 ** @param [u] cache [EnsPCache] Ensembl Cache
767 **
768 ** @return [AjBool] ajTrue upon success, ajFalse otherwise
769 **
770 ** @release 6.5.0
771 ** @@
772 ** Value data in Cache Node objects that have not been synchronised are
773 ** written-back. Cache flags are reset for value data before the value data
774 ** is deleted.
775 ******************************************************************************/
776 
ensCacheClear(EnsPCache cache)777 AjBool ensCacheClear(EnsPCache cache)
778 {
779     CachePNode node = NULL;
780 
781     if (!cache)
782         return ajFalse;
783 
784     /* Remove Cache Node objects from the AJAX List. */
785 
786     while (ajListPop(cache->List, (void **) &node))
787     {
788         /* Remove the same Cache Node object from the AJAX Table. */
789 
790         (void) ajTableRemove(cache->Table, node->Key);
791 
792         /* Update the cache statistics. */
793 
794         cache->Count--;
795 
796         cache->Bytes -= node->Bytes;
797 
798         /* Write changes of value data to disk if any. */
799 
800         if (cache->Fwrite && node->Value && node->Dirty)
801             (*cache->Fwrite) (node->Value);
802 
803         /* Both, key and value data are deleted via cacheNodeDel. */
804 
805         cacheNodeDel(cache, &node);
806     }
807 
808     return ajTrue;
809 }
810 
811 
812 
813 
814 /* @section debugging *********************************************************
815 **
816 ** Functions for reporting of an Ensembl Cache object.
817 **
818 ** @fdata [EnsPCache]
819 **
820 ** @nam3rule Trace Report Ensembl Cache members to debug file.
821 **
822 ** @argrule Trace cache [const EnsPCache] Ensembl Cache
823 ** @argrule Trace level [ajuint] Indentation level
824 **
825 ** @valrule * [AjBool] ajTrue upon success, ajFalse otherwise
826 **
827 ** @fcategory misc
828 ******************************************************************************/
829 
830 
831 
832 
833 /* @func ensCacheTrace ********************************************************
834 **
835 ** Writes debug messages to trace the contents of a cache.
836 **
837 ** @param [r] cache [const EnsPCache] Ensembl Cache
838 ** @param [r] level [ajuint] Indentation level
839 **
840 ** @return [AjBool] ajTrue upon success, ajFalse otherwise
841 **
842 ** @release 6.2.0
843 ** @@
844 ******************************************************************************/
845 
ensCacheTrace(const EnsPCache cache,ajuint level)846 AjBool ensCacheTrace(const EnsPCache cache, ajuint level)
847 {
848     double ratio = 0.0;
849 
850     AjPStr indent = NULL;
851 
852     if (!cache)
853         return ajFalse;
854 
855     indent = ajStrNew();
856 
857     ajStrAppendCountK(&indent, ' ', level * 2);
858 
859     if (cache->Hit || cache->Miss)
860         ratio = (double) cache->Hit /
861             ((double) cache->Hit + (double) cache->Miss);
862 
863     ajDebug("%SensCache trace %p\n"
864             "%S  Label '%S'\n"
865             "%S  List %p length: %Lu\n"
866             "%S  Table %p length: %Lu\n"
867             "%S  Type %d\n"
868             "%S  Synchron '%B'\n"
869             "%S  MaxBytes %Lu\n" /* FIXME: size_t can be shorter than ajulong */
870             "%S  MaxCount %u\n"
871             "%S  MaxSize %Lu\n" /* FIXME: size_t can be shorter than ajulong */
872             "%S  Bytes %Lu\n" /* FIXME: size_t can be shorter than ajulong */
873             "%S  Count %u\n"
874             "%S  Dropped %u\n"
875             "%S  Removed %u\n"
876             "%S  Stored %u\n"
877             "%S  Hit %u\n"
878             "%S  Miss %u\n"
879             "%S  Hit/(Hit + Miss) %f\n",
880             indent, cache,
881             indent, cache->Label,
882             indent, cache->List, ajListGetLength(cache->List),
883             indent, cache->Table, ajTableGetLength(cache->Table),
884             indent, cache->Type,
885             indent, cache->Synchron,
886             indent, cache->MaxBytes,
887             indent, cache->MaxCount,
888             indent, cache->MaxSize,
889             indent, cache->Bytes,
890             indent, cache->Count,
891             indent, cache->Dropped,
892             indent, cache->Removed,
893             indent, cache->Stored,
894             indent, cache->Hit,
895             indent, cache->Miss,
896             indent, ratio);
897 
898     ajStrDel(&indent);
899 
900     return ajTrue;
901 }
902 
903 
904 
905 
906 /* @section modify ************************************************************
907 **
908 ** Update cache object values
909 **
910 ** @fdata [EnsPCache]
911 **
912 ** @nam3rule Fetch Fetch value data from an Ensembl Cache
913 ** @nam3rule Remove Remove value data from an Ensembl Cache
914 ** @nam3rule Store Insert a value into an Ensembl Cache
915 ** @nam3rule Synchronise Synchronise an Ensembl Cache
916 **
917 ** @argrule Fetch cache [EnsPCache] Ensembl Cache
918 ** @argrule Fetch key [void*] Key data address
919 ** @argrule Fetch Pvalue [void**] Value data address address
920 ** @argrule Remove cache [EnsPCache] Ensembl Cache
921 ** @argrule Remove key [const void*] Key data address
922 ** @argrule Store cache [EnsPCache] Ensembl Cache
923 ** @argrule Store key [void*] Key data address
924 ** @argrule Store Pvalue [void**] Value data address adress
925 ** @argrule Synchronise cache [EnsPCache] Ensembl Cache
926 **
927 ** @valrule * [AjBool] ajTrue upon success, ajFalse otherwise
928 **
929 ** @fcategory modify
930 ******************************************************************************/
931 
932 
933 
934 
935 /* @func ensCacheFetch ********************************************************
936 **
937 ** Fetch a value from an Ensembl Cache via a key. If the value is not already
938 ** in the cache it will be read by the function provided at the Cache
939 ** initialisation stage.
940 **
941 ** The caller is responsible for deleting the returned object.
942 **
943 ** @param [u] cache [EnsPCache] Ensembl Cache
944 ** @param [r] key [void*] Key data address
945 ** @param [wP] Pvalue [void**] Value data address address
946 **
947 ** @return [AjBool] ajTrue upon success, ajFalse otherwise
948 **
949 ** @release 6.2.0
950 ** @@
951 ******************************************************************************/
952 
ensCacheFetch(EnsPCache cache,void * key,void ** Pvalue)953 AjBool ensCacheFetch(EnsPCache cache, void *key, void **Pvalue)
954 {
955     AjIList iter = NULL;
956 
957     CachePNode lnode = NULL;
958     CachePNode tnode = NULL;
959 
960     if (!cache)
961         return ajFalse;
962 
963     if (!key)
964         return ajFalse;
965 
966     if (!Pvalue)
967         return ajFalse;
968 
969     *Pvalue = NULL;
970 
971     tnode = (CachePNode) ajTableFetchmodV(cache->Table, key);
972 
973     if (tnode)
974     {
975         cache->Hit++;
976 
977         /* Move the Cache Node to the end of the AJAX List. */
978 
979         iter = ajListIterNew(cache->List);
980 
981         while (!ajListIterDone(iter))
982         {
983             lnode = (CachePNode) ajListIterGet(iter);
984 
985             if (lnode == tnode)
986             {
987                 ajListIterRemove(iter);
988 
989                 ajListPushAppend(cache->List, (void *) lnode);
990 
991                 break;
992             }
993         }
994 
995         ajListIterDel(&iter);
996 
997         /*
998         ** Reference the object when returned by the cache so that external
999         ** code has to delete it irrespectively whether it was read from the
1000         ** cache or instantiated by the cache->Fread function.
1001         */
1002 
1003         if (cache->Freference && tnode->Value)
1004             *Pvalue = (*cache->Freference) (tnode->Value);
1005     }
1006     else
1007     {
1008         cache->Miss++;
1009 
1010         if (cache->Fread)
1011         {
1012             *Pvalue = (*cache->Fread) (key);
1013 
1014             if (*Pvalue)
1015             {
1016                 tnode = cacheNodeNew(cache, key, *Pvalue);
1017 
1018                 if (!cacheNodeInsert(cache, tnode))
1019                     cacheNodeDel(cache, &tnode);
1020             }
1021         }
1022     }
1023 
1024     return ajTrue;
1025 }
1026 
1027 
1028 
1029 
1030 /* @func ensCacheRemove *******************************************************
1031 **
1032 ** Remove value data from an Ensembl Cache via key data.
1033 **
1034 ** @param [u] cache [EnsPCache] Ensembl Cache
1035 ** @param [r] key [const void*] Key data address
1036 **
1037 ** @return [AjBool] ajTrue upon success, ajFalse otherwise
1038 **
1039 ** @release 6.2.0
1040 ** @@
1041 ******************************************************************************/
1042 
ensCacheRemove(EnsPCache cache,const void * key)1043 AjBool ensCacheRemove(EnsPCache cache, const void *key)
1044 {
1045     CachePNode node = NULL;
1046 
1047     if (!cache)
1048         return ajFalse;
1049 
1050     if (!key)
1051         return ajFalse;
1052 
1053     node = (CachePNode) ajTableFetchmodV(cache->Table, key);
1054 
1055     if (node)
1056     {
1057         cacheNodeRemove(cache, node);
1058 
1059         /* Both, key and value data are deleted via cacheNodeDel. */
1060 
1061         cacheNodeDel(cache, &node);
1062     }
1063 
1064     return ajTrue;
1065 }
1066 
1067 
1068 
1069 
1070 /* @func ensCacheStore ********************************************************
1071 **
1072 ** Insert value data into an Ensembl Cache under key data.
1073 **
1074 ** @param [u] cache [EnsPCache] Ensembl Cache
1075 ** @param [u] key [void*] Key data address
1076 ** @param [w] Pvalue [void**] Value data address address
1077 **
1078 ** @return [AjBool] ajTrue upon success, ajFalse otherwise
1079 **
1080 ** @release 6.2.0
1081 ** @@
1082 ******************************************************************************/
1083 
ensCacheStore(EnsPCache cache,void * key,void ** Pvalue)1084 AjBool ensCacheStore(EnsPCache cache, void *key, void **Pvalue)
1085 {
1086     CachePNode node = NULL;
1087 
1088     if (!cache)
1089         return ajFalse;
1090 
1091     if (!key)
1092         return ajFalse;
1093 
1094     if (!Pvalue)
1095         return ajFalse;
1096 
1097     /* Is a node already cached under this key? */
1098 
1099     node = (CachePNode) ajTableFetchmodV(cache->Table, key);
1100 
1101     if (node)
1102     {
1103         /*
1104         ** Delete the Object passed in and increase the reference counter
1105         ** of the cached Object before assigning it.
1106         */
1107 
1108         (*cache->Fdelete) (Pvalue);
1109 
1110         *Pvalue = (*cache->Freference) (node->Value);
1111     }
1112     else
1113     {
1114         node = cacheNodeNew(cache, key, *Pvalue);
1115 
1116         if (cacheNodeInsert(cache, node))
1117         {
1118             if (cache->Synchron)
1119             {
1120                 if (cache->Fwrite && node->Value)
1121                     (*cache->Fwrite) (node->Value);
1122 
1123                 node->Dirty = ajFalse;
1124             }
1125             else
1126                 node->Dirty = ajTrue;
1127         }
1128         else
1129         {
1130             if (cache->Fwrite && node->Value)
1131                 (*cache->Fwrite) (node->Value);
1132 
1133             cacheNodeDel(cache, &node);
1134         }
1135     }
1136 
1137     return ajTrue;
1138 }
1139 
1140 
1141 
1142 
1143 /* @func ensCacheSynchronise **************************************************
1144 **
1145 ** Synchronise an Ensembl Cache by writing-back all value data that have not
1146 ** been written before.
1147 **
1148 ** @param [u] cache [EnsPCache] Ensembl Cache
1149 **
1150 ** @return [AjBool] ajTrue upon success, ajFalse otherwise
1151 **
1152 ** @release 6.2.0
1153 ** @@
1154 ******************************************************************************/
1155 
ensCacheSynchronise(EnsPCache cache)1156 AjBool ensCacheSynchronise(EnsPCache cache)
1157 {
1158     AjIList iter = NULL;
1159 
1160     CachePNode node = NULL;
1161 
1162     if (!cache)
1163         return ajFalse;
1164 
1165     iter = ajListIterNew(cache->List);
1166 
1167     while (!ajListIterDone(iter))
1168     {
1169         node = (CachePNode) ajListIterGet(iter);
1170 
1171         if (cache->Fwrite && node->Value && node->Dirty)
1172         {
1173             (*cache->Fwrite) (node->Value);
1174 
1175             node->Dirty = ajFalse;
1176         }
1177     }
1178 
1179     ajListIterDel(&iter);
1180 
1181     return ajTrue;
1182 }
1183