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 ------------------------------------------------------------------------------
58 #include "rmr_symtab.h"
60 //-----------------------------------------------------------------------------------------------
62 typedef struct Sym_ele
64 struct Sym_ele *next; /* pointer at next element in list */
65 struct Sym_ele *prev; /* larger table, easier deletes */
66 const char *name; /* symbol name */
67 uint64_t nkey; // the numeric key
68 void *val; /* user data associated with name */
69 unsigned long mcount; /* modificaitons to value */
70 unsigned long rcount; /* references to symbol */
72 unsigned int class; /* helps divide things up and allows for duplicate names */
75 typedef struct Sym_tab {
76 Sym_ele **symlist; /* pointer to list of element pointerss */
77 long inhabitants; /* number of active residents */
78 long deaths; /* number of deletes */
82 // -------------------- internal ------------------------------------------------------------------
84 static int sym_hash( const char *n, long size )
91 for( p = n; *p; p++ ) /* a bit of magic */
97 return (int ) (t % size);
101 Delete element pointed to by eptr which is assumed to be
102 a member of the list at symtab[i].
104 static void del_ele( Sym_tab *table, int hv, Sym_ele *eptr )
108 sym_tab = table->symlist;
110 if( eptr ) /* unchain it */
113 eptr->prev->next = eptr->next;
115 sym_tab[hv] = eptr->next;
118 eptr->next->prev = eptr->prev;
120 if( eptr->class && eptr->name ) { // class 0 entries are numeric, so name is NOT a pointer
121 free( (void *) eptr->name ); // and if free fails, what? panic?
127 table->inhabitants--;
132 Delete the head element from table[i]. This isn't really
133 needed, but keeps code analysers from claiming that memory
134 is being used after it is freed.
136 static void del_head_ele( Sym_tab *table, int hv ) {
138 Sym_ele *eptr; // first in list
141 if( hv < 0 || hv >= table->size ) {
145 sym_tab = table->symlist;
146 if( (eptr = sym_tab[hv]) != NULL ) // not an empty element; yank it off
148 if( (sym_tab[hv] = eptr->next) != NULL ) { // bump list to next if not the only thing here
149 sym_tab[hv]->prev = NULL; // new head
151 eptr->next = NULL; // take no chances
153 if( eptr->class && eptr->name ) { // class 0 entries are numeric, so name is NOT a pointer
154 free( (void *) eptr->name );
160 table->inhabitants--;
165 Determine if these are the same.
167 static inline int same( unsigned int c1, unsigned int c2, const char *s1, const char* s2 )
170 return 0; /* different class - not the same */
172 if( *s1 == *s2 && strcmp( s1, s2 ) == 0 )
178 Generic routine to put something into the table
179 called by sym_map or sym_put since the logic for each is pretty
182 static int putin( Sym_tab *table, const char *name, unsigned int class, void *val ) {
183 Sym_ele *eptr; /* pointer into hash table */
184 Sym_ele **sym_tab; /* pointer into hash table */
185 int hv; /* hash value */
186 int rc = 0; /* assume it existed */
187 uint64_t nkey = 0; // numeric key if class == 0
189 sym_tab = table->symlist;
191 if( class ) { // string key
192 hv = sym_hash( name, table->size ); // hash it
193 for( eptr=sym_tab[hv]; eptr && ! same( class, eptr->class, eptr->name, name); eptr=eptr->next );
195 nkey = *((uint64_t *) name);
196 hv = nkey % table->size; // just hash the number
197 for( eptr=sym_tab[hv]; eptr && eptr->nkey != nkey; eptr=eptr->next );
200 if( ! eptr ) { // not found above, so add
202 table->inhabitants++;
204 eptr = (Sym_ele *) malloc( sizeof( Sym_ele) );
212 eptr->mcount = eptr->rcount = 0; /* init counters */
216 eptr->name = strdup( name );
218 eptr->name = NULL; // for a numeric key, just save the value
220 eptr->next = sym_tab[hv]; // add to head of list
223 eptr->next->prev = eptr; /* chain back to new one */
233 // -------------------- visible ------------------------------------------------------------------
235 /* delete all elements in the table */
236 extern void rmr_sym_clear( void *vtable )
242 table = (Sym_tab *) vtable;
243 sym_tab = table->symlist;
245 for( i = 0; i < table->size; i++ ) {
246 while( sym_tab[i] ) {
247 del_head_ele( table, i ); // delete the head element (and keep bloody sonar from claiming use after free)
253 Clear and then free the whole thing.
255 extern void rmr_sym_free( void *vtable ) {
258 table = (Sym_tab *) vtable;
263 rmr_sym_clear( vtable );
264 free( table->symlist );
268 extern void rmr_sym_dump( void *vtable )
275 table = (Sym_tab *) vtable;
276 sym_tab = table->symlist;
278 for( i = 0; i < table->size; i++ )
281 for( eptr = sym_tab[i]; eptr; eptr = eptr->next )
283 if( eptr->val && eptr->class ) {
284 fprintf( stderr, "symtab dump: key=%s val@=%p\n", eptr->name, eptr->val );
286 fprintf( stderr, "symtab dump: nkey=%lu val@=%p\n", (unsigned long) eptr->nkey, eptr->val );
293 Allocate a table the size requested - best if size is prime.
294 Returns a pointer to the management block (handle) or NULL on failure.
296 extern void *rmr_sym_alloc( int size )
301 if( size < 11 ) /* provide a bit of sanity */
304 if( (table = (Sym_tab *) malloc( sizeof( Sym_tab ))) == NULL )
310 memset( table, 0, sizeof( *table ) );
312 if((table->symlist = (Sym_ele **) malloc( sizeof( Sym_ele *) * size )))
314 memset( table->symlist, 0, sizeof( Sym_ele *) * size );
323 return (void *) table; /* user might want to know what the size is */
327 Delete an element given name/class or numeric key (class 0).
329 extern void rmr_sym_del( void *vtable, const char *name, unsigned int class )
333 Sym_ele *eptr; /* pointer into hash table */
334 int hv; /* hash value */
335 uint64_t nkey; // class 0, name points to integer not string
337 table = (Sym_tab *) vtable;
338 sym_tab = table->symlist;
341 hv = sym_hash( name, table->size );
342 for(eptr=sym_tab[hv]; eptr && ! same(class, eptr->class, eptr->name, name); eptr=eptr->next );
344 nkey = *((uint64_t *) name);
345 hv = nkey % table->size; // just hash the number
346 for( eptr=sym_tab[hv]; eptr && eptr->nkey != nkey; eptr=eptr->next );
349 del_ele( table, hv, eptr ); /* ignors null ptr, so safe to always call */
353 Delete element by numberic key.
355 extern void rmr_sym_ndel( void *vtable, uint64_t key ) {
356 rmr_sym_del( vtable, (const char *) &key, 0 );
360 extern void *rmr_sym_get( void *vtable, const char *name, unsigned int class )
364 Sym_ele *eptr; // element from table
365 int hv; // hash value of key
366 uint64_t nkey; // numeric key if class 0
368 if( (table = (Sym_tab *) vtable) == NULL ) {
372 sym_tab = table->symlist;
375 hv = sym_hash( name, table->size );
376 for(eptr=sym_tab[hv]; eptr && ! same(class, eptr->class, eptr->name, name); eptr=eptr->next );
378 nkey = *((uint64_t *) name);
379 hv = nkey % table->size; // just hash the number
380 for( eptr=sym_tab[hv]; eptr && eptr->nkey != nkey; eptr=eptr->next );
393 Retrieve the data referenced by a numerical key.
395 extern void *rmr_sym_pull( void *vtable, uint64_t key ) {
396 return rmr_sym_get( vtable, (const char *) &key, 0 );
400 Put an element with a string key into the table. Replaces the element
401 if it was already there. Class must be >0 and if not 1 will be forced.
402 (class 0 keys are numeric).
403 Returns 1 if new, 0 if existed.
405 extern int rmr_sym_put( void *vtable, const char *name, unsigned int class, void *val )
413 table = (Sym_tab *) vtable;
414 return putin( table, name, class, val );
418 Add a new entry assuming that the key is an unsigned, 64 bit, integer.
420 Returns 1 if new, 0 if existed
422 extern int rmr_sym_map( void *vtable, uint64_t key, void *val ) {
425 table = (Sym_tab *) vtable;
426 return putin( table, (const char *) &key, 0, val );
430 Dump some statistics to stderr dev. Higher level is the more info dumpped
432 extern void rmr_sym_stats( void *vtable, int level )
435 Sym_ele *eptr; /* pointer into the elements */
444 table = (Sym_tab *) vtable;
445 sym_tab = table->symlist;
447 for( i = 0; i < table->size; i++ )
452 for( eptr = sym_tab[i]; eptr; eptr = eptr->next )
456 if( eptr->class ) { // a string key
457 fprintf( stderr, " symtab stats: sym: (%d) key=%s val@=%p ref=%ld mod=%lu\n", i, eptr->name, eptr->val, eptr->rcount, eptr->mcount );
459 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 );
467 if( ch_count > max_chain )
469 max_chain = ch_count;
476 fprintf( stderr, "symtab stats: sym: (%d) chained=%ld\n", i, ch_count );
481 fprintf( stderr, "symtab stats: sym: longest chain: idx=%d has %ld elsements):\n", maxi, max_chain );
482 for( eptr = sym_tab[maxi]; eptr; eptr = eptr->next ) {
484 fprintf( stderr, "\t%s\n", eptr->name );
486 fprintf( stderr, "\t%lu (numeric key)\n", (unsigned long) eptr->nkey );
491 fprintf( stderr, "symtab stats: sym:%ld(size) %ld(inhab) %ld(occupied) %ld(dead) %ld(maxch) %d(>2per)\n",
492 table->size, table->inhabitants, table->size - empty, table->deaths, max_chain, twoper );
496 Drive a user callback function for each entry in a class. It is safe for
497 the user to delete the element as we capture a next pointer before
498 calling their function.
500 extern void rmr_sym_foreach_class( void *vst, unsigned int class, void (* user_fun)( void*, void*, const char*, void*, void* ), void *user_data )
505 Sym_ele *next; /* allows user to delete the node(s) we return */
508 st = (Sym_tab *) vst;
510 if( st && (list = st->symlist) != NULL && user_fun != NULL ) {
511 for( i = 0; i < st->size; i++ ) {
514 next = se->next; // allow callback to delete from the list w/o borking us
515 if( class == se->class ) {
516 user_fun( st, se, se->name, se->val, user_data );