1 // : vi ts=4 sw=4 noet :
3 ==================================================================================
4 Copyright (c) 2019 Nokia
5 Copyright (c) 2018-2019 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.
37 Author: E. Scott Daniels
39 Mod: 2016 23 Feb - converted Symtab refs so that caller need only a
40 void pointer to use and struct does not need to be exposed.
41 2018 30 Nov - Augmented from original form (see above).
42 ------------------------------------------------------------------------------
52 #include "rmr_symtab.h"
54 //-----------------------------------------------------------------------------------------------
56 typedef struct Sym_ele
58 struct Sym_ele *next; /* pointer at next element in list */
59 struct Sym_ele *prev; /* larger table, easier deletes */
60 const char *name; /* symbol name */
61 uint64_t nkey; // the numeric key
62 void *val; /* user data associated with name */
63 unsigned long mcount; /* modificaitons to value */
64 unsigned long rcount; /* references to symbol */
66 unsigned int class; /* helps divide things up and allows for duplicate names */
69 typedef struct Sym_tab {
70 Sym_ele **symlist; /* pointer to list of element pointerss */
71 long inhabitants; /* number of active residents */
72 long deaths; /* number of deletes */
76 // -------------------- internal ------------------------------------------------------------------
78 static int sym_hash( const char *n, long size )
85 for( p = n; *p; p++ ) /* a bit of magic */
91 return (int ) (t % size);
94 /* delete element pointed to by eptr at hash loc hv */
95 static void del_ele( Sym_tab *table, int hv, Sym_ele *eptr )
99 sym_tab = table->symlist;
101 if( eptr ) /* unchain it */
104 eptr->prev->next = eptr->next;
106 sym_tab[hv] = eptr->next;
109 eptr->next->prev = eptr->prev;
111 if( eptr->class && eptr->name ) { // class 0 entries are numeric, so name is NOT a pointer
112 free( (void *) eptr->name ); // and if free fails, what? panic?
118 table->inhabitants--;
123 Determine if these are the same.
125 static inline int same( unsigned int c1, unsigned int c2, const char *s1, const char* s2 )
128 return 0; /* different class - not the same */
130 if( *s1 == *s2 && strcmp( s1, s2 ) == 0 )
136 Generic routine to put something into the table
137 called by sym_map or sym_put since the logic for each is pretty
140 static int putin( Sym_tab *table, const char *name, unsigned int class, void *val ) {
141 Sym_ele *eptr; /* pointer into hash table */
142 Sym_ele **sym_tab; /* pointer into hash table */
143 int hv; /* hash value */
144 int rc = 0; /* assume it existed */
145 uint64_t nkey = 0; // numeric key if class == 0
147 sym_tab = table->symlist;
149 if( class ) { // string key
150 hv = sym_hash( name, table->size ); // hash it
151 for( eptr=sym_tab[hv]; eptr && ! same( class, eptr->class, eptr->name, name); eptr=eptr->next );
153 nkey = *((uint64_t *) name);
154 hv = nkey % table->size; // just hash the number
155 for( eptr=sym_tab[hv]; eptr && eptr->nkey != nkey; eptr=eptr->next );
158 if( ! eptr ) { // not found above, so add
160 table->inhabitants++;
162 eptr = (Sym_ele *) malloc( sizeof( Sym_ele) );
164 fprintf( stderr, "[FAIL] symtab/putin: out of memory\n" );
170 eptr->mcount = eptr->rcount = 0; /* init counters */
174 eptr->name = strdup( name );
176 eptr->name = NULL; // for a numeric key, just save the value
178 eptr->next = sym_tab[hv]; // add to head of list
181 eptr->next->prev = eptr; /* chain back to new one */
191 // -------------------- visible ------------------------------------------------------------------
193 /* delete all elements in the table */
194 extern void rmr_sym_clear( void *vtable )
200 table = (Sym_tab *) vtable;
201 sym_tab = table->symlist;
203 for( i = 0; i < table->size; i++ )
205 del_ele( table, i, sym_tab[i] );
209 Clear and then free the whole thing.
211 extern void rmr_sym_free( void *vtable ) {
214 table = (Sym_tab *) vtable;
219 rmr_sym_clear( vtable );
220 free( table->symlist );
224 extern void rmr_sym_dump( void *vtable )
231 table = (Sym_tab *) vtable;
232 sym_tab = table->symlist;
234 for( i = 0; i < table->size; i++ )
237 for( eptr = sym_tab[i]; eptr; eptr = eptr->next )
239 if( eptr->val && eptr->class ) {
240 fprintf( stderr, "key=%s val@=%p\n", eptr->name, eptr->val );
242 fprintf( stderr, "nkey=%lu val@=%p\n", (unsigned long) eptr->nkey, eptr->val );
249 Allocate a table the size requested - best if size is prime.
250 Returns a pointer to the management block (handle) or NULL on failure.
252 extern void *rmr_sym_alloc( int size )
257 if( size < 11 ) /* provide a bit of sanity */
260 if( (table = (Sym_tab *) malloc( sizeof( Sym_tab ))) == NULL )
262 fprintf( stderr, "rmr_sym_alloc: unable to get memory for symtable (%d elements)", size );
266 memset( table, 0, sizeof( *table ) );
268 if((table->symlist = (Sym_ele **) malloc( sizeof( Sym_ele *) * size )))
270 memset( table->symlist, 0, sizeof( Sym_ele *) * size );
275 fprintf( stderr, "sym_alloc: unable to get memory for %d elements", size );
279 return (void *) table; /* user might want to know what the size is */
283 Delete an element given name/class or numeric key (class 0).
285 extern void rmr_sym_del( void *vtable, const char *name, unsigned int class )
289 Sym_ele *eptr; /* pointer into hash table */
290 int hv; /* hash value */
291 uint64_t nkey; // class 0, name points to integer not string
293 table = (Sym_tab *) vtable;
294 sym_tab = table->symlist;
297 hv = sym_hash( name, table->size );
298 for(eptr=sym_tab[hv]; eptr && ! same(class, eptr->class, eptr->name, name); eptr=eptr->next );
300 nkey = *((uint64_t *) name);
301 hv = nkey % table->size; // just hash the number
302 for( eptr=sym_tab[hv]; eptr && eptr->nkey != nkey; eptr=eptr->next );
305 del_ele( table, hv, eptr ); /* ignors null ptr, so safe to always call */
309 Delete element by numberic key.
311 extern void *rmr_sym_ndel( void *vtable, uint64_t key ) {
312 rmr_sym_del( vtable, (const char *) &key, 0 );
316 extern void *rmr_sym_get( void *vtable, const char *name, unsigned int class )
320 Sym_ele *eptr; // element from table
321 int hv; // hash value of key
322 uint64_t nkey; // numeric key if class 0
324 table = (Sym_tab *) vtable;
325 sym_tab = table->symlist;
328 hv = sym_hash( name, table->size );
329 for(eptr=sym_tab[hv]; eptr && ! same(class, eptr->class, eptr->name, name); eptr=eptr->next );
331 nkey = *((uint64_t *) name);
332 hv = nkey % table->size; // just hash the number
333 for( eptr=sym_tab[hv]; eptr && eptr->nkey != nkey; eptr=eptr->next );
346 Retrieve the data referenced by a numerical key.
348 extern void *rmr_sym_pull( void *vtable, uint64_t key ) {
349 return rmr_sym_get( vtable, (const char *) &key, 0 );
353 Put an element with a string key into the table. Replaces the element
354 if it was already there. Class must be >0 and if not 1 will be forced.
355 (class 0 keys are numeric).
356 Returns 1 if new, 0 if existed.
358 extern int rmr_sym_put( void *vtable, const char *name, unsigned int class, void *val )
366 table = (Sym_tab *) vtable;
367 return putin( table, name, class, val );
371 Add a new entry assuming that the key is an unsigned, 64 bit, integer.
373 Returns 1 if new, 0 if existed
375 extern int rmr_sym_map( void *vtable, uint64_t key, void *val ) {
378 table = (Sym_tab *) vtable;
379 return putin( table, (const char *) &key, 0, val );
383 Dump some statistics to stderr dev. Higher level is the more info dumpped
385 extern void rmr_sym_stats( void *vtable, int level )
388 Sym_ele *eptr; /* pointer into the elements */
397 table = (Sym_tab *) vtable;
398 sym_tab = table->symlist;
400 for( i = 0; i < table->size; i++ )
405 for( eptr = sym_tab[i]; eptr; eptr = eptr->next )
409 if( eptr->class ) { // a string key
410 fprintf( stderr, "sym: (%d) key=%s val@=%p ref=%ld mod=%lu\n", i, eptr->name, eptr->val, eptr->rcount, eptr->mcount );
412 fprintf( stderr, "sym: (%d) key=%lu val@=%p ref=%ld mod=%lu\n", i, (unsigned long) eptr->nkey, eptr->val, eptr->rcount, eptr->mcount );
420 if( ch_count > max_chain )
422 max_chain = ch_count;
429 fprintf( stderr, "sym: (%d) chained=%ld\n", i, ch_count );
434 fprintf( stderr, "sym: longest chain: idx=%d has %ld elsements):\n", maxi, max_chain );
435 for( eptr = sym_tab[maxi]; eptr; eptr = eptr->next ) {
437 fprintf( stderr, "\t%s\n", eptr->name );
439 fprintf( stderr, "\t%lu (numeric key)\n", (unsigned long) eptr->nkey );
444 fprintf( stderr, "sym:%ld(size) %ld(inhab) %ld(occupied) %ld(dead) %ld(maxch) %d(>2per)\n",
445 table->size, table->inhabitants, table->size - empty, table->deaths, max_chain, twoper );
449 Drive a user callback function for each entry in a class. It is safe for
450 the user to delete the element as we capture a next pointer before
451 calling their function.
453 extern void rmr_sym_foreach_class( void *vst, unsigned int class, void (* user_fun)( void*, void*, const char*, void*, void* ), void *user_data )
458 Sym_ele *next; /* allows user to delete the node(s) we return */
461 st = (Sym_tab *) vst;
463 if( st && (list = st->symlist) != NULL && user_fun != NULL )
464 for( i = 0; i < st->size; i++ )
465 for( se = list[i]; se; se = next ) /* using next allows user to delet via this */
468 if( class == se->class ) {
469 user_fun( st, se, se->name, se->val, user_data );