/* ******************************************************************************* * Copyright (c) 1996 Martin Poole * ******************************************************************************* ** ** WARNING !! WARNING !! WARNING !! WARNING !! WARNING !! WARNING !! ** ** Any changes to be made to this file should first be checked with ** mplib1 source control for library integrity. ** ** mplib1 source control can be reached at mplib1@quatermass.co.uk ** * * $Source$ * $Author$ * $Date$ * $Revision$ * ******************************************************************************* * * Change History * * $Log$ * ******************************************************************************* */ #ident "$Header$" /* ------------------------------------------------------------------ Include files ------------------------------------------------------------------ */ #include #include #include #include #include #include #include #include #include #include #include #include #include "../include/dl_lru_private.h" /* ------------------------------------------------------------------ defines ------------------------------------------------------------------ */ struct vchar_gen { unsigned short len; unsigned char arr[1]; }; typedef struct vchar_gen *vchar_ptr; struct cps { struct Cache_List *cl; int nrl; struct full_search_rule *srp; void **sv; struct Cache_Item *ci; struct Cache_Item *si; struct Cache_Item *rv; }; /* ------------------------------------------------------------------ Some static variables ------------------------------------------------------------------ */ static List_t *Master_List=NULL; static int default_flags=CACHE_USE_DEFAULT; #ifdef CACHE_DEBUG static int cache_debug=0; #endif #define SEARCH_MISSING_LIST BIT(0) #define SEARCH_FREE_LIST BIT(1) #define SEARCH_LRU_LIST BIT(2) #define SEARCH_ALL_LISTS (SEARCH_MISSING_LIST|SEARCH_FREE_LIST|SEARCH_LRU_LIST) /* ------------------------------------------------------------------ Code starts here ------------------------------------------------------------------ */ struct Full_List * Init_Full_List ( struct Full_List *list, const char *name, int flags ) { size_t ms=(size_t)0; char *mp=NULL; if ( list==NULL ) ms += FULL_LIST_SIZE; if ( name ) ms += (size_t)(strlen(name) + 1); if ( ms && ( mp = (char *)malloc( ms )) == NULL ) return(NULL); if ( list==NULL ) { list = (struct Full_List *) mp; mp += FULL_LIST_SIZE; } if ( name ) { strcpy( mp , name ); name = mp; } Init_List( &list->fl_Search_List, flags ); Init_List( &list->fl_LRU_List, flags ); list->fl_Name = name; return(list); } void * Find_Full_Item ( struct Full_List *flist, const char *name ) { void *item; dl_Node_t *node; if ( (item = Find_Item_By_Name( &flist->fl_Search_List, name )) ) { if( (node = Find_Node_By_Item( &flist->fl_LRU_List, item )) ) { Add_Head( &flist->fl_LRU_List, node ); } } return(item); } static int Local_Cache_Init( List_t **cml ) { List_t *ml=*cml; int rv=0; static int init_done=0; if ( (ml == NULL) && (ml = (List_t *)malloc(LIST_SIZE)) ) { *cml = ml; Init_List ( ml, 0 ); init_done = 1; rv=1; } return(rv); } List_t * Get_Cache_Master_List( void ) { Local_Cache_Init(&Master_List); return( Master_List ); } static int Cache_Flag_Gen( int new_flags, int alt_flags ) { int cl_flags=CACHE_USE_DEFAULT; int t_flags; /* the object here is to generate a set of cache flags that is the composite of the default and the new and that there is a bit set in each grouping In order for the lines to read simply (and since they are just variations of each other) a macro will now be defined */ #define C_MODE(r,n,a,t,m,d) {t= (n & m) ? (n & m) : (a & m);if (t==m) t= (d & m); r |= t;} C_MODE(cl_flags,new_flags,alt_flags,t_flags,CACHE_MODE_MASK,CACHE_DEFAULTS); C_MODE(cl_flags,new_flags,alt_flags,t_flags,CACHE_CASE_MASK,CACHE_DEFAULTS); C_MODE(cl_flags,new_flags,alt_flags,t_flags,CACHE_ALLOC_MASK,CACHE_DEFAULTS); C_MODE(cl_flags,new_flags,alt_flags,t_flags,CACHE_MISS_MASK,CACHE_DEFAULTS); C_MODE(cl_flags,new_flags,alt_flags,t_flags,CACHE_INSERT_MASK,CACHE_DEFAULTS); C_MODE(cl_flags,new_flags,alt_flags,t_flags,CACHE_FLUSH_MASK,CACHE_DEFAULTS); return(cl_flags); } int Cache_Init( int def_flags) { if (default_flags == CACHE_USE_DEFAULT) { default_flags = Cache_Flag_Gen( def_flags, CACHE_DEFAULTS ); #ifdef CACHE_DEBUG if (def_flags & 0x40000000) { cache_debug=1; fprintf(stderr,"cache debug is on\n"); } #endif } return(Local_Cache_Init(&Master_List)); } struct Cache_Item * Cache_Find_Simple_Item( struct Cache_List *cl, const char *list_key, int *rv ) { struct Cache_Item *ci; ci = (struct Cache_Item *) Find_Full_Item( &cl->cl_Items, list_key ); if (ci) { *rv = CACHE_FOUND_ITEM; }else { /* check the complete miss list */ ci = (struct Cache_Item *) Find_Item_By_Name( &cl->cl_Missing, list_key ); *rv = (ci) ? CACHE_COMPLETE_MISS : CACHE_MISSED; ci=NULL; } return(ci); } int Find_Cache_Item( const char *list_name, const void *list_key, const char *field_list, ... ) { int rv=CACHE_MISSED; struct Cache_List *cl; struct Cache_Item *ci; struct Cache_Field *cf; va_list ap; void *vp,*ftok1; char *cp; char *field_list_copy; vchar_ptr vcp1,vcp2; static char sep_list[]=" \t\r\n,"; if (Master_List==NULL) Local_Cache_Init(&Master_List); /* just in case we haven't done it before */ #ifdef CACHE_DEBUG if (cache_debug) fprintf(stderr,"Find_Cache_Item: <%s> <%p> ", list_name, list_key ); #endif cl = (struct Cache_List *)Find_Item_By_Name( Master_List, list_name ); if (cl) { if ( cl->cl_search_rules != NULL ) { ci = Cache_Find_Requested_Item( cl, list_key, &rv ); }else { ci = Cache_Find_Simple_Item( cl, list_key, &rv ); /* ci = (struct Cache_Item *) Find_Full_Item( &cl->cl_Items, list_key );*/ } if ( ci != NULL ) { #ifdef CACHE_DEBUG if (cache_debug) fprintf(stderr,"Found\n"); #endif /* now to transfer the relevant fields we do this by parsing the field list using strtok, and then finding the named field in the cf list, and then finding the item field with the corresponding field_num the value is then returned into the next vararg */ cl->cl_cache_hits++; if (cl->cl_flags & CACHE_CACHE_ON) { field_list_copy = strdup(field_list); va_start(ap,field_list); cp=mpstrtok(field_list_copy,&ftok1,sep_list); while (cp) { vp = va_arg(ap, void* ); /* find this in the cf list */ cf = (struct Cache_Field *)Find_Item_By_Name( &cl->cl_Fields, cp ); if (cf) { /* now generate a pointer to the data in the data segment */ cp = (char *)(ci->ci_Item) + cf->cf_f_offset; /* we know where it is and what it is, so copy it */ switch(cf->cf_field_type) { case CF_INT : *((int *)vp) = *((int *)cp); break; case CF_DOUBLE : *((double *)vp) = *((double *)cp); break; case CF_CHAR : *((char *)vp) = *cp; break; case CF_STRING : strcpy( (char *)vp, cp ); break; case CF_VARCHAR : vcp1 = (vchar_ptr)vp; vcp2 = (vchar_ptr)cp; vcp1->len = vcp2->len; memcpy(vcp1->arr, vcp2->arr, vcp2->len ); break; } } rv=CACHE_FOUND_ITEM; cp = mpstrtok( NULL, &ftok1, sep_list); }; (void)mpstrtok( NULL, &ftok1, NULL ); va_end( ap ); } }else { cl->cl_cache_misses++; #ifdef CACHE_DEBUG if (cache_debug) fprintf(stderr,"Not Found\n"); #endif } } #ifdef CACHE_DEBUG else if (cache_debug) fprintf(stderr," List not Found\n"); #endif return(rv); } static int Init_Item( struct Cache_Item *ci, struct Cache_List *cl, char *new_name, size_t name_len ) { if (ci && cl) { /* inititalise the various parts of this beast */ Init_Node( &ci->ci_S_Node, new_name, ci ); Init_Node( &ci->ci_L_Node, new_name, ci ); ci->ci_Name = new_name; ci->ci_str_size = name_len; ci->ci_Item = (void *) (((char *)ci) + CACHE_ITEM_SIZE); /* cl->cl_curr_items++;*/ /* Add_Head( &cl->cl_Items.fl_LRU_List, &ci->ci_L_Node );*/ /* Add_Head( &cl->cl_Items.fl_Search_List, &ci->ci_S_Node );*/ } return(0); } void * Cache_Alloc_Lump( size_t sz, const char *name, char **new_name ) { void *vp; char *np; size_t osz = sz; if (name) sz += (size_t)(strlen(name) + 1); vp = (void *)malloc( sz ); if (vp) { memset( vp, '\0', sz ); if (name) { np = (char *)vp + osz; strcpy(np,name); if (new_name) *new_name = np; } } return(vp); } static int Alloc_Many( struct Cache_List *cl, size_t key_size ) { struct Cache_Item *ci; size_t osz,asz; int ni,rv=0; char *cp,*np; if ( cl->cl_max_items > 0 ) { if (key_size < 1) key_size = 40; osz = CACHE_ITEM_SIZE + cl->cl_data_size + key_size + 1; osz += (osz & 1); /* adjust object size to next word boundary */ asz = osz * cl->cl_max_items; if ( (cp=malloc(asz)) ) { memset( cp, '\0', asz ); /* now init each item and store on list */ ni = cl->cl_max_items; while(ni--) { ci = (struct Cache_Item *)cp; np = cp + CACHE_ITEM_SIZE + cl->cl_data_size; Init_Item( ci, cl, np, key_size ); Add_Head( &cl->cl_Free, &ci->ci_S_Node ); cp += osz; }; } } return(rv); } struct Cache_List * New_Cache_List( const char *list_name, int max_items, int flags, int defaults, int last_ditch ) { struct Cache_List *cl; char *new_name; cl = Cache_Alloc_Lump( CACHE_LIST_SIZE, list_name, &new_name ); if (cl) { /* inititalise the various parts of this beast */ Init_Node( &cl->cl_Node, new_name, cl ); cl->cl_Name = new_name; /* now generate the cl-flags */ cl->cl_flags = Cache_Flag_Gen( Cache_Flag_Gen( flags, defaults ), last_ditch ); Init_List( &cl->cl_Fields, (cl->cl_flags & CACHE_IGNORE_CASE)?LN_IGNORECASE:0 ); Init_List( &cl->cl_Free, 0 ); Init_List( &cl->cl_Free_cgi, 0 ); Init_List( &cl->cl_Build, 0 ); Init_List( &cl->cl_Missing, 0 ); Init_Full_List( &cl->cl_Items, NULL, ((cl->cl_flags & CACHE_IGNORE_CASE)?LN_IGNORECASE:0) ); cl->cl_max_field_num = 0; cl->cl_max_items = max_items; cl->cl_curr_items = 0; cl->cl_cache_replaces = 0; cl->cl_cache_hits = 0; cl->cl_cache_misses = 0; cl->cl_complete_misses = 0; cl->cl_data_size = 0; cl->cl_search_rules = NULL; } return(cl); } int Cache_Build_Field_List( struct Cache_List *cl, const char *format_str, const char *field_names ) { int rv=0; struct Cache_Field *cf; char *fnames = NULL; char *fstr = NULL; char *cp,*cp2; void *ftok1,*ftok2; static char fs_sep[]="%"; static char fn_sep[]=","; int dsz=0; /* count up the space used by the data */ int psz; /* parameter size so far */ int ft; /* field type */ int flags; /* Various flags. eg Ignore case when matching? */ int align; if ( (fstr = strdup(format_str)) && (fnames = strdup(field_names)) ) { /* Now parse and allocate Field structures */ cp = mpstrtok( fstr, &ftok1, fs_sep ); cp2 = mpstrtok( fnames, &ftok2, fn_sep ); while (cp && cp2) { /* grab any size specifier */ psz = 0; ft = 0; flags = 0; align=0; while(isdigit(*cp)) psz = psz * 10 + (*cp++ & 0x0f); if (*cp=='i' || *cp=='I') { flags |= CF_FLG_IGNORECASE; cp++; } switch(*cp) { case 's' : ft = CF_STRING; if (psz == 0) psz = 40; break; case 'c' : ft = CF_CHAR; psz = sizeof(char); flags &= ~CF_FLG_IGNORECASE; align=MPLIB_ALIGN_SPC_CHAR; break; case 'd' : case 'x' : ft = CF_INT; psz = sizeof(int); flags &= ~CF_FLG_IGNORECASE; align=MPLIB_ALIGN_SPC_INT; break; case 'f' : ft = CF_DOUBLE; psz = sizeof(double); flags &= ~CF_FLG_IGNORECASE; align=MPLIB_ALIGN_SPC_DOUBLE; break; case 'S' : ft = CF_VARCHAR; if (psz == 0) psz = 40; psz += sizeof(unsigned short); break; } if ( ft && (cf = Cache_Alloc_Lump( CACHE_FIELD_SIZE, cp2, &cp )) ) { /* do something with this */ Init_Node( &cf->cf_Node, cp, cf ); cf->cf_Name = cp; cf->cf_field_num = cl->cl_max_field_num++; cf->cf_field_type = ft; if (align && (dsz & (align-1))) { /* Adjust data position to ensure correct alignment */ dsz = (dsz + align -1) & ~(align-1); } cf->cf_f_offset = dsz; cf->cf_field_size = psz; cf->cf_field_flags = flags; dsz += ( (psz + 1) & ~1); /* adjust size to next word boundary */ cl->cl_data_size = dsz; Add_Tail( &cl->cl_Fields, &cf->cf_Node ); rv = 1; }else { /* Something wrong, throw everything away and return error */ while ( (cf=Remove_Head_Item(&cl->cl_Fields)) ) free(cf); rv = 0; break; } cp = mpstrtok( NULL, &ftok1, fs_sep ); cp2 = mpstrtok( NULL, &ftok2, fn_sep ); }; (void) mpstrtok( NULL, &ftok1, NULL ); (void) mpstrtok( NULL, &ftok2, NULL ); } if (fstr) free(fstr); if (fnames) free(fnames); return(rv); } int Create_Cache_List( const char *list_name, int max_items, int list_flags, size_t key_size, const char *format_str, const char *field_names ) { int rv=0; struct Cache_List *cl; Local_Cache_Init(&Master_List); /* just in case we haven't done it before */ #ifdef CACHE_DEBUG if (cache_debug) { fprintf(stderr,"Create_Cache_List: <%s> <%d> <%s> <%s> ", list_name,max_items,format_str,field_names); fflush(stderr); } #endif cl = (struct Cache_List *)Find_Item_By_Name( Master_List, list_name ); if ( cl == NULL && (cl=New_Cache_List( list_name, max_items,list_flags, default_flags, CACHE_DEFAULTS )) ) { rv = Cache_Build_Field_List( cl, format_str, field_names ); if (rv==1 && (cl->cl_flags & CACHE_PRE_ALLOCATE) && cl->cl_max_items > 0 ) { /* time to pre-allocate the cache items */ Alloc_Many( cl, key_size ); } if (rv==1) Add_Head( Master_List, &cl->cl_Node ); } #ifdef CACHE_DEBUG if (cache_debug) fprintf(stderr,"%s\n",(rv)?"successful":"failed"); #endif return(rv); } static struct Cache_Item * New_Cache_Item( struct Cache_List *cl, char *item_name ) { struct Cache_Item *ci; char *new_name; ci = Cache_Alloc_Lump( CACHE_ITEM_SIZE + cl->cl_data_size, item_name, &new_name ); if (ci) { /* inititalise the various parts of this beast */ Init_Item( ci, cl, new_name, strlen(new_name) ); } return(ci); } static struct Cache_Item * Obtain_Cache_Item( struct Cache_List *cl, const char *list_key, List_t *L_List, int i_flags ) { struct Cache_Item *ci=NULL; /* anything on the free queue? */ if ( (i_flags & SEARCH_MISSING_LIST) && (cl->cl_search_rules==NULL) && list_key ) ci = Find_Item_By_Name( &cl->cl_Missing, list_key ); if ( ci==NULL && (i_flags & SEARCH_FREE_LIST) ) ci = Remove_Head_Item( &cl->cl_Free ); if (ci) { /* adjust ? */ cl->cl_curr_items++; if (list_key && strlen(list_key) > ci->ci_str_size ) { cl->cl_curr_items--; Add_Tail( &cl->cl_Free, &ci->ci_S_Node ); ci = NULL; }else { if (list_key) strcpy(ci->ci_Name, list_key); memset(ci->ci_Item, '\0', ci->ci_data_size ); } } /* alloc new item or re-use */ if ( ci==NULL && L_List && (i_flags & SEARCH_LRU_LIST) && cl->cl_max_items && cl->cl_curr_items >= cl->cl_max_items ) { /* Re-Use last on LRU chain */ ci = Remove_Tail_Item( L_List ); if (ci) { cl->cl_cache_replaces++; Remove_Node( &ci->ci_S_Node ); /* and remove from search chain */ if ( strlen(list_key) > ci->ci_str_size ) { cl->cl_curr_items--; Add_Tail( &cl->cl_Free, &ci->ci_S_Node ); ci = NULL; }else { /* re-using, so adjust name */ if (list_key) strcpy(ci->ci_Name, list_key); memset(ci->ci_Item, '\0', ci->ci_data_size ); } } } if (ci==NULL) { ci = New_Cache_Item( cl, list_key ); } return(ci); } int Add_Cache_Item( const char *list_name, const void *list_key, ... ) { struct Cache_List *cl; struct Cache_Item *ci; struct Cache_Field *cf; void *vp; vchar_ptr vcp1,vcp2; int rv=0; va_list ap; Local_Cache_Init(&Master_List); /* just in case we haven't done it before */ #ifdef CACHE_DEBUG if (cache_debug) fprintf(stderr,"Add_Cache_Item: <%s> <%p> ", list_name, list_key ); #endif cl = (struct Cache_List *)Find_Item_By_Name( Master_List, list_name ); if (cl) { if (list_key == CACHE_ONE_OF_ONE) { /* get an item and be ready to use it */ ci = Cache_Get_Item_From_Free( cl ); }else if (list_key == CACHE_END_OF_MANY) { /* time to link all those created so far into the real list */ return( Cache_End_Of_Many( cl ) ); } else if (list_key == CACHE_ONE_OF_MANY) { /* get an item and be ready to use it */ ci = Cache_Get_Item_From_Free( cl ); }else { ci = Find_Full_Item( &cl->cl_Items, list_key ); if (ci==NULL) { ci = Obtain_Cache_Item( cl, list_key, &cl->cl_Items.fl_LRU_List, (SEARCH_ALL_LISTS) ); if (ci) { /* If we got an item, it is hanging free so... Put it on the relevant lists */ Add_Head( &cl->cl_Items.fl_LRU_List, &ci->ci_L_Node ); Add_Head( &cl->cl_Items.fl_Search_List, &ci->ci_S_Node ); cl->cl_curr_items++; } } } if (ci) { #ifdef CACHE_DEBUG if (cache_debug) fprintf(stderr,"Added\n"); #endif /* now insert the new values */ cf = (struct Cache_Field *)cl->cl_Fields.ln_Head.ln_Succ; va_start(ap, list_key ); while (cf->cf_Node.ln_Succ) { switch( cf->cf_field_type ) { case CF_INT : /* fprintf(stderr,"Fetch int ");*/ *(int *)((char *)ci->ci_Item + cf->cf_f_offset) = (int)va_arg(ap, int ); /* fprintf(stderr,"- done\n");*/ break; case CF_DOUBLE : /* fprintf(stderr,"Fetch double ");*/ *(double *)((char *)ci->ci_Item + cf->cf_f_offset) = (double)va_arg(ap, double ); /* fprintf(stderr,"- done\n");*/ break; case CF_CHAR : /* fprintf(stderr,"Fetch char ");*/ *(char *)((char *)ci->ci_Item + cf->cf_f_offset) = (char)va_arg(ap, char ); /* fprintf(stderr,"- done\n");*/ break; case CF_STRING : /* fprintf(stderr,"Fetch string ");*/ vp = va_arg(ap, void* ); strcpy( (char *)ci->ci_Item + cf->cf_f_offset, (char *)vp ); /* fprintf(stderr,"- done\n");*/ break; case CF_VARCHAR : /* fprintf(stderr,"Fetch varchar ");*/ vp = va_arg(ap, vchar_ptr ); vcp1 = (vchar_ptr)((char *)ci->ci_Item + cf->cf_f_offset); vcp2 = (vchar_ptr)vp; vcp1->len = vcp2->len; memcpy(vcp1->arr, vcp2->arr, vcp2->len ); /* fprintf(stderr,"- done\n");*/ break; } cf = (struct Cache_Field *)cf->cf_Node.ln_Succ; rv=1; }; va_end(ap); if (list_key == CACHE_ONE_OF_ONE) { /* get an item and be ready to use it */ rv = Cache_End_Of_Many( cl ); } /*hex_dump(stderr, ci->ci_Item, cl->cl_data_size );*/ } #ifdef CACHE_DEBUG else if (cache_debug) fprintf(stderr,"not added\n"); #endif } #ifdef CACHE_DEBUG else if (cache_debug) fprintf(stderr,"List not found\n"); #endif return(rv); } int Cache_Complete_Miss( const char *list_name, const void* list_key ) { struct Cache_List *cl; struct Cache_Item *ci; Node_t *node; Local_Cache_Init(&Master_List); /* just in case we haven't done it before */ #ifdef CACHE_DEBUG if (cache_debug) fprintf(stderr,"Cache_Complete_Miss: "); #endif cl = (struct Cache_List *)Find_Item_By_Name( Master_List, list_name ); if (cl) { cl->cl_complete_misses ++; if ( cl->cl_search_rules != NULL ) { Range_Complete_Miss( cl, list_key ); }else { ci = Find_Item_By_Name( &cl->cl_Missing, list_key ); if (ci) { node = Find_Node_By_Item( &cl->cl_Missing, ci ); Remove_Node( node ); Add_Head( &cl->cl_Missing, node ); } else { /* Allocate a node and add it to the missing chain */ ci = Remove_Head_Item( &cl->cl_Free ); if (ci==NULL) ci = Remove_Head_Item( &cl->cl_Missing ); if (ci==NULL) { /* allocate one ? */ } if (ci) { /* put on complete miss list */ Add_Head( &cl->cl_Missing, &ci->ci_S_Node ); } } } #ifdef CACHE_DEBUG if (cache_debug) fprintf(stderr,"OK\n"); #endif } #ifdef CACHE_DEBUG else if (cache_debug) fprintf(stderr,"Failed\n"); #endif return(1); } int Cache_Flush( const char *list_name ) { struct Cache_List *cl; struct Cache_Item *ci; int rv=0; Local_Cache_Init(&Master_List); /* just in case we haven't done it before */ #ifdef CACHE_DEBUG if (cache_debug) fprintf(stderr,"Cache_Flush: "); #endif cl = (struct Cache_List *)Find_Item_By_Name( Master_List, list_name ); if (cl && (cl->cl_flags & CACHE_FLUSHABLE) ) { if ( cl->cl_search_rules != NULL ) { rv = Range_Flush( cl ); }else { while ( (ci = Remove_Head_Item( &cl->cl_Items.fl_Search_List )) ) { Remove_Node( &ci->ci_L_Node ); Add_Tail( &cl->cl_Free, &ci->ci_S_Node ); }; while( (ci = Remove_Head_Item( &cl->cl_Missing )) ) { Add_Tail( &cl->cl_Free, &ci->ci_S_Node ); } cl->cl_curr_items = 0; rv = 1; } } #ifdef CACHE_DEBUG else if (cache_debug) fprintf(stderr,"Failed\n"); #endif return(rv); } /* -- End of File -- */