1 // : vi ts=4 sw=4 noet :
3 ==================================================================================
4 Copyright (c) 2019-2020 Nokia
5 Copyright (c) 2018-2020 AT&T Intellectual Property.
7 Licensed under the Apache License, Version 2.0 (the "License");
8 you may not use this file except in compliance with the License.
9 You may obtain a copy of the License at
11 http://www.apache.org/licenses/LICENSE-2.0
13 Unless required by applicable law or agreed to in writing, software
14 distributed under the License is distributed on an "AS IS" BASIS,
15 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 See the License for the specific language governing permissions and
17 limitations under the License.
18 ==================================================================================
22 ------------------------------------------------------------------------------
24 Abstract: Symbol table -- slightly streamlined from it's original 2000 version
25 (a part of the {X}fm opensource code), though we must retain the
28 Things changed for the Ric Msg implemention (Nov 2018):
29 - no concept of copy/free of the user data (functions removed)
30 - add ability to support an integer key (class 0)
31 Numeric key is an unsigned, 64bit key.
32 - externally visible names given a rmr_ extension as it's being
33 incorporated into the RIC msg routing library and will be
34 available to user applications.
36 There is NO logging from this module! The caller is asusmed to
37 report any failures as it might handle them making any error messages
38 generated here misleading if not incorrect.
41 Author: E. Scott Daniels
43 Mod: 2016 23 Feb - converted Symtab refs so that caller need only a
44 void pointer to use and struct does not need to be exposed.
45 2018 30 Nov - Augmented from original form (see above).
46 ------------------------------------------------------------------------------
57 #include "rmr_symtab.h"
59 //-----------------------------------------------------------------------------------------------
61 typedef struct Sym_ele
63 struct Sym_ele *next; /* pointer at next element in list */
64 struct Sym_ele *prev; /* larger table, easier deletes */
65 const char *name; /* symbol name */
66 uint64_t nkey; // the numeric key
67 void *val; /* user data associated with name */
68 unsigned long mcount; /* modificaitons to value */
69 unsigned long rcount; /* references to symbol */
71 unsigned int class; /* helps divide things up and allows for duplicate names */
74 typedef struct Sym_tab {
75 Sym_ele **symlist; /* pointer to list of element pointerss */
76 long inhabitants; /* number of active residents */
77 long deaths; /* number of deletes */
81 // -------------------- internal ------------------------------------------------------------------
83 static int sym_hash( const char *n, long size )
90 for( p = n; *p; p++ ) /* a bit of magic */
96 return (int ) (t % size);
100 Delete element pointed to by eptr which is assumed to be
101 a member of the list at symtab[i].
103 static void del_ele( Sym_tab *table, int hv, Sym_ele *eptr )
107 sym_tab = table->symlist;
109 if( eptr ) /* unchain it */
112 eptr->prev->next = eptr->next;
114 sym_tab[hv] = eptr->next;
117 eptr->next->prev = eptr->prev;
119 if( eptr->class && eptr->name ) { // class 0 entries are numeric, so name is NOT a pointer
120 free( (void *) eptr->name ); // and if free fails, what? panic?
126 table->inhabitants--;
131 Delete the head element from table[i]. This isn't really
132 needed, but keeps code analysers from claiming that memory
133 is being used after it is freed.
135 static void del_head_ele( Sym_tab *table, int hv ) {
137 Sym_ele *eptr; // first in list
140 if( hv < 0 || hv >= table->size ) {
144 sym_tab = table->symlist;
145 if( (eptr = sym_tab[hv]) != NULL ) // not an empty element; yank it off
147 if( (sym_tab[hv] = eptr->next) != NULL ) { // bump list to next if not the only thing here
148 sym_tab[hv]->prev = NULL; // new head
150 eptr->next = NULL; // take no chances
152 if( eptr->class && eptr->name ) { // class 0 entries are numeric, so name is NOT a pointer
153 free( (void *) eptr->name );
159 table->inhabitants--;
164 Determine if these are the same.
166 static inline int same( unsigned int c1, unsigned int c2, const char *s1, const char* s2 )
169 return 0; /* different class - not the same */
171 if( *s1 == *s2 && strcmp( s1, s2 ) == 0 )
177 Generic routine to put something into the table
178 called by sym_map or sym_put since the logic for each is pretty
181 static int putin( Sym_tab *table, const char *name, unsigned int class, void *val ) {
182 Sym_ele *eptr; /* pointer into hash table */
183 Sym_ele **sym_tab; /* pointer into hash table */
184 int hv; /* hash value */
185 int rc = 0; /* assume it existed */
186 uint64_t nkey = 0; // numeric key if class == 0
188 sym_tab = table->symlist;
190 if( class ) { // string key
191 hv = sym_hash( name, table->size ); // hash it
192 for( eptr=sym_tab[hv]; eptr && ! same( class, eptr->class, eptr->name, name); eptr=eptr->next );
194 nkey = *((uint64_t *) name);
195 hv = nkey % table->size; // just hash the number
196 for( eptr=sym_tab[hv]; eptr && eptr->nkey != nkey; eptr=eptr->next );
199 if( ! eptr ) { // not found above, so add
201 table->inhabitants++;
203 eptr = (Sym_ele *) malloc( sizeof( Sym_ele) );
211 eptr->mcount = eptr->rcount = 0; /* init counters */
215 eptr->name = strdup( name );
217 eptr->name = NULL; // for a numeric key, just save the value
219 eptr->next = sym_tab[hv]; // add to head of list
222 eptr->next->prev = eptr; /* chain back to new one */
232 // -------------------- visible ------------------------------------------------------------------
234 /* delete all elements in the table */
235 extern void rmr_sym_clear( void *vtable )
241 table = (Sym_tab *) vtable;
242 sym_tab = table->symlist;
244 for( i = 0; i < table->size; i++ ) {
245 while( sym_tab[i] ) {
246 del_head_ele( table, i ); // delete the head element (and keep bloody sonar from claiming use after free)
252 Clear and then free the whole thing.
254 extern void rmr_sym_free( void *vtable ) {
257 table = (Sym_tab *) vtable;
262 rmr_sym_clear( vtable );
263 free( table->symlist );
267 extern void rmr_sym_dump( void *vtable )
274 table = (Sym_tab *) vtable;
275 sym_tab = table->symlist;
277 for( i = 0; i < table->size; i++ )
280 for( eptr = sym_tab[i]; eptr; eptr = eptr->next )
282 if( eptr->val && eptr->class ) {
283 fprintf( stderr, "symtab dump: key=%s val@=%p\n", eptr->name, eptr->val );
285 fprintf( stderr, "symtab dump: nkey=%lu val@=%p\n", (unsigned long) eptr->nkey, eptr->val );
292 Allocate a table the size requested - best if size is prime.
293 Returns a pointer to the management block (handle) or NULL on failure.
295 extern void *rmr_sym_alloc( int size )
300 if( size < 11 ) /* provide a bit of sanity */
303 if( (table = (Sym_tab *) malloc( sizeof( Sym_tab ))) == NULL )
309 memset( table, 0, sizeof( *table ) );
311 if((table->symlist = (Sym_ele **) malloc( sizeof( Sym_ele *) * size )))
313 memset( table->symlist, 0, sizeof( Sym_ele *) * size );
322 return (void *) table; /* user might want to know what the size is */
326 Delete an element given name/class or numeric key (class 0).
328 extern void rmr_sym_del( void *vtable, const char *name, unsigned int class )
332 Sym_ele *eptr; /* pointer into hash table */
333 int hv; /* hash value */
334 uint64_t nkey; // class 0, name points to integer not string
336 table = (Sym_tab *) vtable;
337 sym_tab = table->symlist;
340 hv = sym_hash( name, table->size );
341 for(eptr=sym_tab[hv]; eptr && ! same(class, eptr->class, eptr->name, name); eptr=eptr->next );
343 nkey = *((uint64_t *) name);
344 hv = nkey % table->size; // just hash the number
345 for( eptr=sym_tab[hv]; eptr && eptr->nkey != nkey; eptr=eptr->next );
348 del_ele( table, hv, eptr ); /* ignors null ptr, so safe to always call */
352 Delete element by numberic key.
354 extern void rmr_sym_ndel( void *vtable, uint64_t key ) {
355 rmr_sym_del( vtable, (const char *) &key, 0 );
359 extern void *rmr_sym_get( void *vtable, const char *name, unsigned int class )
363 Sym_ele *eptr; // element from table
364 int hv; // hash value of key
365 uint64_t nkey; // numeric key if class 0
367 if( (table = (Sym_tab *) vtable) == NULL ) {
371 sym_tab = table->symlist;
374 hv = sym_hash( name, table->size );
375 for(eptr=sym_tab[hv]; eptr && ! same(class, eptr->class, eptr->name, name); eptr=eptr->next );
377 nkey = *((uint64_t *) name);
378 hv = nkey % table->size; // just hash the number
379 for( eptr=sym_tab[hv]; eptr && eptr->nkey != nkey; eptr=eptr->next );
392 Retrieve the data referenced by a numerical key.
394 extern void *rmr_sym_pull( void *vtable, uint64_t key ) {
395 return rmr_sym_get( vtable, (const char *) &key, 0 );
399 Put an element with a string key into the table. Replaces the element
400 if it was already there. Class must be >0 and if not 1 will be forced.
401 (class 0 keys are numeric).
402 Returns 1 if new, 0 if existed.
404 extern int rmr_sym_put( void *vtable, const char *name, unsigned int class, void *val )
412 table = (Sym_tab *) vtable;
413 return putin( table, name, class, val );
417 Add a new entry assuming that the key is an unsigned, 64 bit, integer.
419 Returns 1 if new, 0 if existed
421 extern int rmr_sym_map( void *vtable, uint64_t key, void *val ) {
424 table = (Sym_tab *) vtable;
425 return putin( table, (const char *) &key, 0, val );
429 Dump some statistics to stderr dev. Higher level is the more info dumpped
431 extern void rmr_sym_stats( void *vtable, int level )
434 Sym_ele *eptr; /* pointer into the elements */
443 table = (Sym_tab *) vtable;
444 sym_tab = table->symlist;
446 for( i = 0; i < table->size; i++ )
451 for( eptr = sym_tab[i]; eptr; eptr = eptr->next )
455 if( eptr->class ) { // a string key
456 fprintf( stderr, " symtab stats: sym: (%d) key=%s val@=%p ref=%ld mod=%lu\n", i, eptr->name, eptr->val, eptr->rcount, eptr->mcount );
458 fprintf( stderr, "symtab stats: sym: (%d) key=%lu val@=%p ref=%ld mod=%lu\n", i, (unsigned long) eptr->nkey, eptr->val, eptr->rcount, eptr->mcount );
466 if( ch_count > max_chain )
468 max_chain = ch_count;
475 fprintf( stderr, "symtab stats: sym: (%d) chained=%ld\n", i, ch_count );
480 fprintf( stderr, "symtab stats: sym: longest chain: idx=%d has %ld elsements):\n", maxi, max_chain );
481 for( eptr = sym_tab[maxi]; eptr; eptr = eptr->next ) {
483 fprintf( stderr, "\t%s\n", eptr->name );
485 fprintf( stderr, "\t%lu (numeric key)\n", (unsigned long) eptr->nkey );
490 fprintf( stderr, "symtab stats: sym:%ld(size) %ld(inhab) %ld(occupied) %ld(dead) %ld(maxch) %d(>2per)\n",
491 table->size, table->inhabitants, table->size - empty, table->deaths, max_chain, twoper );
495 Drive a user callback function for each entry in a class. It is safe for
496 the user to delete the element as we capture a next pointer before
497 calling their function.
499 extern void rmr_sym_foreach_class( void *vst, unsigned int class, void (* user_fun)( void*, void*, const char*, void*, void* ), void *user_data )
504 Sym_ele *next; /* allows user to delete the node(s) we return */
507 st = (Sym_tab *) vst;
509 if( st && (list = st->symlist) != NULL && user_fun != NULL ) {
510 for( i = 0; i < st->size; i++ ) {
513 next = se->next; // allow callback to delete from the list w/o borking us
514 if( class == se->class ) {
515 user_fun( st, se, se->name, se->val, user_data );