// // aegis - project change supervisor // Copyright (C) 1998, 2002-2008, 2012 Peter Miller // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see // . // #include #include #include #include // // NAME // itab_alloc // // SYNOPSIS // itab_ty *itabLalloc(int); // // DESCRIPTION // The itab_alloc function is used to allocate a new integer table // instance in dynamic memory. // // RETURNS // itab_ty *; pointer to table // // CAVEAT // Use itab_free when you are done with it. // itab_ty * itab_alloc() { itab_ty *itp; itab_key_ty j; trace(("itab_alloc()\n{\n")); itp = (itab_ty *)mem_alloc(sizeof(itab_ty)); itp->reap = 0; itp->hash_modulus = 1 << 5; // MUST be a power of 2 itp->hash_mask = itp->hash_modulus - 1; itp->load = 0; itp->hash_table = (itab_row_ty **)mem_alloc(itp->hash_modulus * sizeof(itab_row_ty *)); for (j = 0; j < itp->hash_modulus; ++j) itp->hash_table[j] = 0; trace(("return %p;\n", itp)); trace(("}\n")); return itp; } // // NAME // itab_free // // SYNOPSIS // void itab_free(itab_ty *); // // DESCRIPTION // The itab_free function is used to release the resources held by // an integer table. // void itab_free(itab_ty *itp) { itab_key_ty j; trace(("itab_free(itp = %p)\n{\n", itp)); for (j = 0; j < itp->hash_modulus; ++j) { itab_row_ty **rpp; rpp = &itp->hash_table[j]; while (*rpp) { itab_row_ty *rp; rp = *rpp; *rpp = rp->overflow; if (itp->reap) itp->reap(rp->data); mem_free(rp); } } mem_free(itp->hash_table); mem_free(itp); trace(("}\n")); } // // NAME // split - reduce symbol table load // // SYNOPSIS // void split(itab_ty); // // DESCRIPTION // The split function is used to split symbols in the bucket indicated by // the split point. The symbols are split between that bucket and the one // after the current end of the table. // // CAVEAT // It is only sensable to do this when the symbol table load exceeds some // reasonable threshold. A threshold of 80% is suggested. // static void split(itab_ty *itp) { itab_row_ty **new_hash_table; itab_key_ty new_hash_modulus; itab_key_ty new_hash_mask; itab_key_ty idx; trace(("split(itp = %p)\n{\n", itp)); // // double the modulus // // This is subtle. If we only increase the modulus by one, the // load always hovers around 80%, so we have to do a split for // every insert. I.e. the malloc burden os O(n) for the lifetime of // the itab. BUT if we double the modulus, the length of time // until the next split also doubles, making the probablity of a // split halve, and sigma(2**-n)=1, so the malloc burden becomes O(1) // for the lifetime of the itab. // new_hash_modulus = itp->hash_modulus * 2; new_hash_mask = new_hash_modulus - 1; new_hash_table = (itab_row_ty **)mem_alloc(new_hash_modulus * sizeof(itab_row_ty *)); // // now redistribute the list elements // for (idx = 0; idx < itp->hash_modulus; ++idx) { itab_row_ty *p; new_hash_table[idx] = 0; new_hash_table[idx + itp->hash_modulus] = 0; p = itp->hash_table[idx]; while (p) { itab_row_ty *p2; itab_row_ty **ipp; p2 = p; p = p2->overflow; p2->overflow = 0; // // It is important to preserve the order of the links because // they can be push-down stacks, and to simply add them to the // head of the list will reverse the order of the stack! // assert((p2->key & itp->hash_mask) == idx); itab_key_ty idx2 = p2->key & new_hash_mask; for (ipp = &new_hash_table[idx2]; *ipp; ipp = &(*ipp)->overflow) ; *ipp = p2; } } mem_free(itp->hash_table); itp->hash_table = new_hash_table; itp->hash_modulus = new_hash_modulus; itp->hash_mask = new_hash_mask; trace(("}\n")); } // // NAME // itab_query - search for a variable // // SYNOPSIS // int itab_query(itab_ty *, string_ty *key); // // DESCRIPTION // The itab_query function is used to reference a variable. // // RETURNS // If the variable has been defined, the function returns a non-zero value // and the value is returned through the 'value' pointer. // If the variable has not been defined, it returns zero, // and 'value' is unaltered. // void * itab_query(itab_ty *itp, itab_key_ty key) { itab_key_ty idx; itab_row_ty *p; void *result; trace(("itab_query(itp = %p, key = %ld)\n{\n", itp, (long)key)); result = 0; idx = key & itp->hash_mask; for (p = itp->hash_table[idx]; p; p = p->overflow) { if (key == p->key) { result = p->data; break; } } trace(("return %p;\n", result)); trace(("}\n")); return result; } // // NAME // itab_assign - assign a variable // // SYNOPSIS // void itab_assign(itab_ty *, string_ty *key, void *data); // // DESCRIPTION // The itab_assign function is used to assign // a value to a given variable. // // CAVEAT // The name is copied, the data is not. // void itab_assign(itab_ty *itp, itab_key_ty key, void *data) { itab_key_ty idx; itab_row_ty *p; trace(("itab_assign(itp = %p, key = %ld, data = %p)\n{\n", itp, (long)key, data)); idx = key & itp->hash_mask; for (p = itp->hash_table[idx]; p; p = p->overflow) { if (key == p->key) { trace(("modify existing entry\n")); if (itp->reap) itp->reap(p->data); p->data = data; goto done; } } trace(("new entry\n")); p = (itab_row_ty *)mem_alloc(sizeof(itab_row_ty)); p->key = key; p->overflow = itp->hash_table[idx]; p->data = data; itp->hash_table[idx] = p; itp->load++; if (itp->load * 10 >= itp->hash_modulus * 8) split(itp); done: trace(("}\n")); } // // NAME // itab_delete - delete a variable // // SYNOPSIS // void itab_delete(string_ty *name, itab_class_ty class); // // DESCRIPTION // The itab_delete function is used to delete variables. // // CAVEAT // The name is freed, the data is reaped. // (By default, reap does nothing.) // void itab_delete(itab_ty *itp, itab_key_ty key) { itab_key_ty idx; itab_row_ty **pp; trace(("itab_delete(itp = %p, key = %ld)\n{\n", itp, (long)key)); idx = key & itp->hash_mask; pp = &itp->hash_table[idx]; for (;;) { itab_row_ty *p; p = *pp; if (!p) break; if (key == p->key) { if (itp->reap) itp->reap(p->data); *pp = p->overflow; mem_free(p); itp->load--; break; } pp = &p->overflow; } trace(("}\n")); } // // NAME // itab_walk // // SYNOPSIS // void itab_walk(itab_ty *, void (*)(itab_ty *, itab_key_ty, void *, // void *), void *); // // DESCRIPTION // The itab_walk function is used to visit each element of an // integer table, in no particular order. // void itab_walk(itab_ty *itp, void (*func)(itab_ty *, itab_key_ty, void *, void *), void *arg) { long j; itab_row_ty *rp; trace(("itab_walk(itp = %p)\n{\n", itp)); for (j = 0; j < itp->hash_modulus; ++j) for (rp = itp->hash_table[j]; rp; rp = rp->overflow) func(itp, rp->key, rp->data, arg); trace(("}\n")); } // vim: set ts=8 sw=4 et :