/*ident "@(#)cls4:lib/new/_arr_map.c 1.5" */ /******************************************************************************* C++ source for the C++ Language System, Release 3.0. This product is a new release of the original cfront developed in the computer science research center of AT&T Bell Laboratories. Copyright (c) 1993 UNIX System Laboratories, Inc. Copyright (c) 1991, 1992 AT&T and UNIX System Laboratories, Inc. Copyright (c) 1984, 1989, 1990 AT&T. All Rights Reserved. THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE of AT&T and UNIX System Laboratories, Inc. The copyright notice above does not evidence any actual or intended publication of such source code. *******************************************************************************/ // manage the global table of array pointers and counts #include #include // !!!! only works if sizeof(long) >= sizeof(void*) !!!! typedef void* KEYTYP; // generic array address int __insert_new_array(KEYTYP key, int count); // key is a pointer to a new array. It must // be non-zero // not already be in the table // count is the number of elements in the array. May be zero int __remove_old_array(KEYTYP key); // removes an old array from the table. Returns the count or -1 if not found // choose 2 <= DIGIT_SIZE <= 6 // smaller numbers waste less space and are slower, larger numbers waste more // space and are faster // control the size of blocks grabbed from the free store (and frequency of // allocations) with RECORDS_BLOCK_COUNT and NODES_BLOCK_COUNT // a trie based on groups of DIGIT_SIZE bits from the key #define DIGIT_SIZE 5 // each node contains 2 ** DIGIT_SIZE entries #define NODE_SIZE (1 << DIGIT_SIZE) #define MASK_BITS (1 << DIGIT_SIZE) - 1 // a few low order bits are uninteresting and are rotated around to the top #define LOW_BITS 2 #define LOW_MASK ((1 << LOW_BITS) - 1) // the number of groups in a word #define POSITIONS ((BITS(long) - 1) / DIGIT_SIZE + 1) // control the size of blocks grabbed from new #define RECORDS_BLOCK_COUNT 150 #define NODES_BLOCK_COUNT 10 // avoid name space clashes #define pnd __pnd #define Node_Pool __Node_Pool #define Record_Pool __Record_Pool #define pnd_internal_node __pnd_internal_node class pnd; class pnd_internal_node; class RECORD { friend class pnd; friend int __insert_new_array(KEYTYP addr, int count); inline void* operator new(size_t); inline void operator delete(void* p, size_t); inline RECORD(unsigned long k, int cnt); inline ~RECORD(); unsigned long key; // rotated address of array int count; // number of elements of array }; class Record_Pool; class Record_shell { friend class Record_Pool; Record_shell* next; char dummy[sizeof(RECORD) - sizeof(Record_shell*)]; }; class Record_Pool { friend class RECORD; static Record_shell* top; Record_Pool(); Record_shell slot[RECORDS_BLOCK_COUNT]; static void* alloc() { void* ans = 0; if (top == 0) new Record_Pool; if (top != 0) { ans = top; top = top->next; } return ans; } static void free(Record_shell* p) { p->next = top; top = p; } }; Record_shell* Record_Pool::top = 0; // definition of static member inline void* RECORD::operator new(size_t) { return Record_Pool::alloc(); } inline void RECORD::operator delete(void* p, size_t) { Record_Pool::free((Record_shell*)p); } inline RECORD::RECORD(unsigned long k, int cnt) : key(k), count(cnt) {} inline RECORD::~RECORD() {} class pnd_internal_item { friend class pnd; friend class pnd_internal_node; union { RECORD* ext_leaf; pnd_internal_node* nodep; }; int this_is_leaf; int is_node() { return !this_is_leaf && nodep; } int is_leaf() { return this_is_leaf; } int is_null() { return !nodep; } pnd_internal_node* next_node() { return nodep; } RECORD* external_leaf() { return ext_leaf; } void make_leaf(RECORD* p) { this_is_leaf = 1; ext_leaf = p; } void make_node(pnd_internal_node* cp) { this_is_leaf = 0; nodep = cp; } void make_null() {this_is_leaf = 0; nodep = 0; } }; class Node_Pool; class pnd_internal_node { friend class pnd; friend int __insert_new_array(KEYTYP key, int count); inline void* operator new(size_t i); inline void operator delete(void* p, size_t i); pnd_internal_node(); inline ~pnd_internal_node(); pnd_internal_item item[NODE_SIZE]; int busy_count; }; class Node_shell { friend class Node_Pool; Node_shell* next; char dummy[sizeof(pnd_internal_node) - sizeof(Node_shell*)]; }; class Node_Pool { friend class pnd_internal_node; static Node_shell* top; Node_Pool(); Node_shell slot[NODES_BLOCK_COUNT]; static void* alloc() { void* ans = 0; if (top == 0) new Node_Pool; if (top != 0) { ans = top; top = top->next; } return ans; } static void free(void* p) { ((Node_shell*)p)->next = top; top = (Node_shell*)p; } }; Node_shell* Node_Pool::top = 0; // definition of static member inline void* pnd_internal_node::operator new(size_t) { return Node_Pool::alloc(); } inline void pnd_internal_node::operator delete(void* p, size_t) { Node_Pool::free(p); } inline pnd_internal_node::~pnd_internal_node() {} class pnd { friend int __insert_new_array(KEYTYP key, int count); friend int __remove_old_array(KEYTYP key); static pnd* the_table; pnd_internal_item contents; // int sze; pnd(); static void initialize(); int insert(KEYTYP, int); int remove(KEYTYP); // returns count or -1 if not found }; pnd* pnd::the_table = 0; // definition of static member int __insert_new_array(KEYTYP key, int count) { if (pnd::the_table == 0) pnd::initialize(); if (pnd::the_table == 0) return -1; if (pnd::the_table->insert(key, count) == -1) return -1; return 0; } int __remove_old_array(KEYTYP key) { // the conditional resolves a perversion return pnd::the_table == 0 ? -1 : pnd::the_table->remove(key); } pnd_internal_node::pnd_internal_node() { register pnd_internal_item* itemp = &item[0]; register int i = NODE_SIZE; while (i--) (itemp++)->make_null(); busy_count = 0; } int pnd::insert(KEYTYP addr, int cnt) { register unsigned long mask = MASK_BITS; register unsigned long key = (unsigned long)addr & LOW_MASK; if (key) key <<= BITS(long) - LOW_BITS; key |= (unsigned long)addr >> LOW_BITS; RECORD* new_rec = new RECORD(key, cnt); register int unshift = 0; register pnd_internal_item* itemp = &contents; register pnd_internal_node* nodep = 0; if (new_rec == 0) return -1; for (;; mask <<= DIGIT_SIZE, unshift += DIGIT_SIZE) { if (itemp->is_null()) break; if (itemp->is_leaf()) { if (itemp->external_leaf()->key == key){ itemp->external_leaf()->count = cnt; delete new_rec; // didn't need it after all return 0; // should rarely happen } break; } nodep = itemp->next_node(); // assert(nodep); itemp = &nodep->item[(key & mask) >> unshift]; } // sze++; if (itemp->is_null()) { itemp->make_leaf(new_rec); if (nodep) nodep->busy_count++; return 0; } // assert(itemp->is_leaf()); RECORD* temp = itemp->external_leaf(); unsigned long other_key = temp->key; // assert(other_key != key); unsigned long ind1, ind2; for (;; mask <<= DIGIT_SIZE, unshift += DIGIT_SIZE) { nodep = new pnd_internal_node; if (nodep == 0) return -1; itemp->make_node(nodep); // assert(pos.curr_depth < POSITIONS); ind1 = (other_key & mask) >> unshift; ind2 = (key & mask) >> unshift; if (ind1 != ind2) break; // I hope so! nodep->busy_count = 1; itemp = &nodep->item[ind1]; } nodep->item[ind1].make_leaf(temp); nodep->item[ind2].make_leaf(new_rec); nodep->busy_count = 2; return 0; } int pnd::remove(KEYTYP addr) { pnd_internal_item* curr_pos[POSITIONS]; int curr_depth; if (addr == 0) return -1; register unsigned long mask = MASK_BITS; register unsigned long key = (unsigned long)addr & LOW_MASK; if (key) key <<= BITS(long) - LOW_BITS; key |= (unsigned long)addr >> LOW_BITS; register int unshift = 0; register pnd_internal_item* itemp = &contents; register pnd_internal_node* nodep = 0; for (curr_depth = -1;; mask <<= DIGIT_SIZE, unshift += DIGIT_SIZE) { // assert(curr_depth < POSITIONS); if (itemp->is_null()) return -1; if (itemp->is_leaf()) if (itemp->external_leaf()->key == key) break; else return -1; nodep = itemp->next_node(); // assert(nodep); curr_pos[++curr_depth] = itemp = &nodep->item[(key & mask) >> unshift]; } // assert(itemp->is_leaf()); // assert(itemp->external_leaf()->key == key); RECORD* old_p = itemp->external_leaf(); // sze--; int ans = old_p->count; delete old_p; itemp->make_null(); if (curr_depth == -1 || // it was a singleton set --(nodep->busy_count) > 1) // easy case return ans; // assert(nodep->busy_count == 1); register pnd_internal_item* itp = &nodep->item[0]; while (itp->is_null() || itp == itemp) itp++; if (!itp->is_leaf()) // unfortunate case return ans; RECORD* temp = itp->external_leaf(); // assert(temp->key != key); for (;;) { delete nodep; if (curr_depth-- == 0) { // assert(sze == 1); contents.make_leaf(temp); return ans; } nodep = curr_depth == 0 ? contents.next_node() : curr_pos[curr_depth-1]->next_node(); if (nodep->busy_count > 1) { curr_pos[curr_depth]->make_leaf(temp); return ans; } // assert(nodep->busy_count == 1); } } pnd::pnd() /* : sze(0) */ { contents.make_null(); } void pnd::initialize() { the_table = new pnd; } Record_Pool::Record_Pool() { register int i = RECORDS_BLOCK_COUNT; register Record_shell* p = slot; register Record_shell* q; while (--i) { q = p++; q->next = p; } p->next = top; top = slot; } Node_Pool::Node_Pool() { register int i = NODES_BLOCK_COUNT; register Node_shell* p = slot; register Node_shell* q; while (--i) { q = p++; q->next = p; } p->next = top; top = slot; }