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 if( (table = (Sym_tab *) vtable) == NULL ) {
328 sym_tab = table->symlist;
331 hv = sym_hash( name, table->size );
332 for(eptr=sym_tab[hv]; eptr && ! same(class, eptr->class, eptr->name, name); eptr=eptr->next );
334 nkey = *((uint64_t *) name);
335 hv = nkey % table->size; // just hash the number
336 for( eptr=sym_tab[hv]; eptr && eptr->nkey != nkey; eptr=eptr->next );
349 Retrieve the data referenced by a numerical key.
351 extern void *rmr_sym_pull( void *vtable, uint64_t key ) {
352 return rmr_sym_get( vtable, (const char *) &key, 0 );
356 Put an element with a string key into the table. Replaces the element
357 if it was already there. Class must be >0 and if not 1 will be forced.
358 (class 0 keys are numeric).
359 Returns 1 if new, 0 if existed.
361 extern int rmr_sym_put( void *vtable, const char *name, unsigned int class, void *val )
369 table = (Sym_tab *) vtable;
370 return putin( table, name, class, val );
374 Add a new entry assuming that the key is an unsigned, 64 bit, integer.
376 Returns 1 if new, 0 if existed
378 extern int rmr_sym_map( void *vtable, uint64_t key, void *val ) {
381 table = (Sym_tab *) vtable;
382 return putin( table, (const char *) &key, 0, val );
386 Dump some statistics to stderr dev. Higher level is the more info dumpped
388 extern void rmr_sym_stats( void *vtable, int level )
391 Sym_ele *eptr; /* pointer into the elements */
400 table = (Sym_tab *) vtable;
401 sym_tab = table->symlist;
403 for( i = 0; i < table->size; i++ )
408 for( eptr = sym_tab[i]; eptr; eptr = eptr->next )
412 if( eptr->class ) { // a string key
413 fprintf( stderr, "sym: (%d) key=%s val@=%p ref=%ld mod=%lu\n", i, eptr->name, eptr->val, eptr->rcount, eptr->mcount );
415 fprintf( stderr, "sym: (%d) key=%lu val@=%p ref=%ld mod=%lu\n", i, (unsigned long) eptr->nkey, eptr->val, eptr->rcount, eptr->mcount );
423 if( ch_count > max_chain )
425 max_chain = ch_count;
432 fprintf( stderr, "sym: (%d) chained=%ld\n", i, ch_count );
437 fprintf( stderr, "sym: longest chain: idx=%d has %ld elsements):\n", maxi, max_chain );
438 for( eptr = sym_tab[maxi]; eptr; eptr = eptr->next ) {
440 fprintf( stderr, "\t%s\n", eptr->name );
442 fprintf( stderr, "\t%lu (numeric key)\n", (unsigned long) eptr->nkey );
447 fprintf( stderr, "sym:%ld(size) %ld(inhab) %ld(occupied) %ld(dead) %ld(maxch) %d(>2per)\n",
448 table->size, table->inhabitants, table->size - empty, table->deaths, max_chain, twoper );
452 Drive a user callback function for each entry in a class. It is safe for
453 the user to delete the element as we capture a next pointer before
454 calling their function.
456 extern void rmr_sym_foreach_class( void *vst, unsigned int class, void (* user_fun)( void*, void*, const char*, void*, void* ), void *user_data )
461 Sym_ele *next; /* allows user to delete the node(s) we return */
464 st = (Sym_tab *) vst;
466 if( st && (list = st->symlist) != NULL && user_fun != NULL )
467 for( i = 0; i < st->size; i++ )
468 for( se = list[i]; se; se = next ) /* using next allows user to delet via this */
471 if( class == se->class ) {
472 user_fun( st, se, se->name, se->val, user_data );