--- parser3/src/include/pa_hash.h 2009/07/29 05:01:46 1.78 +++ parser3/src/include/pa_hash.h 2017/11/15 22:48:57 1.99 @@ -1,7 +1,7 @@ /** @file Parser: hash class decl. - Copyright (c) 2001-2009 ArtLebedev Group (http://www.artlebedev.com) + Copyright (c) 2001-2017 Art. Lebedev Studio (http://www.artlebedev.com) Author: Alexandr Petrosian (http://paf.design.ru) */ @@ -17,7 +17,7 @@ #ifndef PA_HASH_H #define PA_HASH_H -static const char * const IDENT_HASH_H="$Date: 2009/07/29 05:01:46 $"; +#define IDENT_PA_HASH_H "$Id: pa_hash.h,v 1.99 2017/11/15 22:48:57 moko Exp $" #include "pa_memory.h" #include "pa_types.h" @@ -89,59 +89,69 @@ inline uint hash_code(int self) { #undef HASH #undef HASH_STRING -#undef NEW_PAIR +#undef HASH_NEW_PAIR +#undef HASH_ORDER_CLEAR +#undef HASH_FOR_EACH #define HASH OrderedHash #define HASH_STRING OrderedHashString -#define NEW_PAIR(code, key, value) *ref=new Pair(code, key, value, *ref, this->last); this->last=&((*ref)->next) +#define HASH_NEW_PAIR(code, key, value) *ref=new Pair(code, key, value, *ref, this->last); this->last=&((*ref)->next) +#define HASH_ORDER_CLEAR() first=0; last=&first + +#define HASH_FOR_EACH \ + for(Pair *pair=this->first; pair; pair=pair->next) #else #define HASH Hash #define HASH_STRING HashString -#define NEW_PAIR(code, key, value) *ref=new Pair(code, key, value, *ref) +#define HASH_NEW_PAIR(code, key, value) *ref=new Pair(code, key, value, *ref) +#define HASH_ORDER_CLEAR() + +#define HASH_FOR_EACH \ + Pair **ref=this->refs; \ + for(int index=0; indexallocated; index++) \ + for(Pair *pair=*ref++; pair; pair=pair->link) #endif template class HASH: public PA_Object { +protected: + class Pair; public: - typedef K key_type; typedef V value_type; HASH() { allocated=Hash_allocates[allocates_index=0]; - threshold=allocated*THRESHOLD_PERCENT/100; fpairs_count=fused_refs=0; - refs=new(UseGC) Pair*[allocated]; -#ifdef HASH_ORDER - first=0; - last=&first; -#endif + refs=new(PointerGC) Pair*[allocated]; + HASH_ORDER_CLEAR(); } HASH(const HASH& source) { allocates_index=source.allocates_index; allocated=source.allocated; - threshold=source.threshold; fused_refs=source.fused_refs; fpairs_count=source.fpairs_count; - refs=new(UseGC) Pair*[allocated]; -#ifdef HASH_ORDER - first=0; - last=&first; -#endif + refs=new(PointerGC) Pair*[allocated]; // clone & rehash - Pair **old_ref=source.refs; - for(int index=0; indexlink; - - Pair **ref=&refs[index]; - NEW_PAIR(pair->code, pair->key, pair->value); - - pair=next; +#ifdef HASH_ORDER + HASH_ORDER_CLEAR(); + for(Pair *pair=source.first; pair; pair=pair->next) + { + uint index=pair->code%allocated; + Pair **ref=&refs[index]; + HASH_NEW_PAIR(pair->code, pair->key, pair->value); + } +#else + for(int i=0; ilink) + { + Pair **ref=&refs[i]; + HASH_NEW_PAIR(pair->code, pair->key, pair->value); } +#endif } #ifdef USE_DESTRUCTORS @@ -153,7 +163,7 @@ public: delete pair; pair=next; } - delete[] refs; + operator delete[](refs); } #endif @@ -179,120 +189,11 @@ public: // proper pair not found -- create&link_in new pair if(!*ref) // root cell were fused_refs? fused_refs++; // not, we'll use it and record the fact - NEW_PAIR(code, key, value); + HASH_NEW_PAIR(code, key, value); fpairs_count++; return false; } - /// put a [value] under the [key] @returns existed or not - template R replace_maybe_append(K key, V value, F prevent, I info) { - if(!value) { - // they can come here from somewhere (true with maybe_replace_maybe_append, keeping parallel) - remove(key); - // this has nothing to do with properties, doing no special property handling here - return 0; - } - - if(is_full()) - expand(); - - uint code=hash_code(key); - uint index=code%allocated; - Pair **ref=&refs[index]; - for(Pair *pair=*ref; pair; pair=pair->link) - if(pair->code==code && pair->key==key) { - // found a pair with the same key - pair->value=value; - return reinterpret_cast(1); - } - - // proper pair not found - // prevent-function intercepted append? - if(R result=prevent(value, info)) - return result; - - //create&link_in new pair - if(!*ref) // root cell were fused_refs? - fused_refs++; // not, we'll use it and record the fact - NEW_PAIR(code, key, value); - fpairs_count++; - return 0; - } - - /// put a [value] under the [key] @returns existed or not - template - R maybe_replace_maybe_append(K key, V value, F1 prevent_replace, F2 prevent_append, I info) - { - if(!value) { - // they can come here from Temp_value_element::dctor to restore some empty value - remove(key); - // this has nothing to do with properties, doing no special property handling here - return 0; - } - - if(is_full()) - expand(); - - uint code=hash_code(key); - uint index=code%allocated; - Pair **ref=&refs[index]; - for(Pair *pair=*ref; pair; pair=pair->link) - if(pair->code==code && pair->key==key) { - // found a pair with the same key - - // prevent-function intercepted replace? - if(R result=prevent_replace(pair->value, info)) - return result; - - pair->value=value; - return reinterpret_cast(1); - } - - // proper pair not found - // prevent-function intercepted append? - if(R result=prevent_append(value, info)) - return result; - - //create&link_in new pair - if(!*ref) // root cell were fused_refs? - fused_refs++; // not, we'll use it and record the fact - NEW_PAIR(code, key, value); - fpairs_count++; - return 0; - } - - /// put a [value] under the [key] @returns existed or not - template - R maybe_replace_never_append(K key, V value, F1 prevent_replace, I info) - { - if(!value) { - // they can come here from somewhere (true with maybe_replace_maybe_append, keeping parallel) - remove(key); - // this has nothing to do with properties, doing no special property handling here - return 0; - } - - if(is_full()) - expand(); - - uint code=hash_code(key); - uint index=code%allocated; - Pair **ref=&refs[index]; - for(Pair *pair=*ref; pair; pair=pair->link) - if(pair->code==code && pair->key==key) { - // found a pair with the same key - - // prevent-function intercepted replace? - if(R result=prevent_replace(pair->value, info)) - return result; - - pair->value=value; - return reinterpret_cast(1); - } - - return 0; - } - /// remove the [key] @returns existed or not bool remove(K key) { uint code=hash_code(key); @@ -309,7 +210,6 @@ public: else last=pair->prev; #endif - delete pair; *ref=next; --fpairs_count; return true; @@ -342,49 +242,70 @@ public: return V(0); } - /// put a [value] under the [key] if that [key] existed @returns existed or not - bool put_replaced(K key, V value) { - if(!value) { - remove(key); - return false; +#ifdef HASH_ORDER + String::Body first_key() const { +#ifdef HASH_CODE_CACHING + return (first) ? String::Body(first->key, first->code) : String::Body(); +#else + return (first) ? first->key : String::Body(); +#endif + } + + V first_value() const { + return (first) ? first->value : V(0); + } + + inline Pair* last_pair() const { + return (fpairs_count) ? (Pair*)((char *)last - offsetof(Pair, next)) : NULL; + } + + String::Body last_key() const { + if(Pair* pair = last_pair()) { +#ifdef HASH_CODE_CACHING + return String::Body(pair->key, pair->code); +#else + return pair->key; +#endif + } else { + return String::Body(); } - uint code=hash_code(key); - uint index=code%allocated; - for(Pair *pair=refs[index]; pair; pair=pair->link) - if(pair->code==code && pair->key==key) { - // found a pair with the same key, replacing - pair->value=value; - return true; - } + } - // proper pair not found - return false; + V last_value() const { + if(Pair* pair = last_pair()) + return pair->value; + return NULL; } + void order_clear() { + HASH_ORDER_CLEAR(); + } + + void order_next(Pair* pair) { + pair->prev=last; + pair->next=0; + *last=pair; + last=&(pair->next); + } +#endif //HASH_ORDER + /// put a [value] under the [key] if that [key] existed @returns existed or not - template R maybe_put_replaced(K key, V value, F prevent) { + bool put_replaced(K key, V value) { if(!value) { - // they can come here from Temp_value_element::dctor to restore some empty value remove(key); - // this has nothing to do with properties, doing no special property handling here - return 0; + return false; } - uint code=hash_code(key); uint index=code%allocated; for(Pair *pair=refs[index]; pair; pair=pair->link) if(pair->code==code && pair->key==key) { // found a pair with the same key, replacing - // prevent-function intercepted put? - if(R result=prevent(pair->value)) - return result; - pair->value=value; - return reinterpret_cast(1); + return true; } // proper pair not found - return 0; + return false; } /// put a [value] under the [key] if that [key] NOT existed @returns existed or not @@ -408,17 +329,19 @@ public: // proper pair not found -- create&link_in new pair if(!*ref) // root cell were fused_refs? fused_refs++; // not, we'll use it and record the fact - NEW_PAIR(code, key, value); + HASH_NEW_PAIR(code, key, value); fpairs_count++; return false; } - /** put all 'src' values if NO with same key existed - @todo optimize this.allocated==src.allocated case - */ + /// put all 'src' values if NO with same key existed void merge_dont_replace(const HASH& src) { +#ifdef HASH_ORDER + for(Pair *pair=src.first; pair; pair=pair->next) +#else for(int i=0; ilink) +#endif put_dont_replace(pair->key, pair->value); } @@ -427,72 +350,39 @@ public: /// iterate over all pairs template void for_each(void callback(K, V, I), I info) const { -#ifdef HASH_ORDER - for(Pair *pair=first; pair; pair=pair->next) + HASH_FOR_EACH callback(pair->key, pair->value, info); -#else - Pair **ref=refs; - for(int index=0; indexlink) - callback(pair->key, pair->value, info); -#endif } /// iterate over all pairs template void for_each_ref(void callback(K, V&, I), I info) const { -#ifdef HASH_ORDER - for(Pair *pair=first; pair; pair=pair->next) + HASH_FOR_EACH callback(pair->key, pair->value, info); -#else - Pair **ref=refs; - for(int index=0; indexlink) - callback(pair->key, pair->value, info); -#endif } /// iterate over all pairs until condition becomes true, return that element template V first_that(bool callback(K, V, I), I info) const { -#ifdef HASH_ORDER - for(Pair *pair=first; pair; pair=pair->next) + HASH_FOR_EACH if(callback(pair->key, pair->value, info)) return pair->value; -#else - Pair **ref=refs; - for(int index=0; indexlink) - if(callback(pair->key, pair->value, info)) - return pair->value; -#endif return V(0); } /// remove all elements void clear() { memset(refs, 0, sizeof(*refs)*allocated); - fpairs_count=fused_refs=0; -#ifdef HASH_ORDER - first=0; - last=&first; -#endif + fpairs_count=fused_refs=0; + HASH_ORDER_CLEAR(); } protected: - /// expand when these %% of allocated exausted - enum { - THRESHOLD_PERCENT=75 - }; - /// the index of [allocated] in [Hash_allocates] int allocates_index; /// number of allocated pairs int allocated; - /// helper: expanding when fused_refs == threshold - int threshold; - /// used pairs int fused_refs; @@ -522,19 +412,18 @@ protected: Pair **last; #endif - /// filled to threshold: needs expanding - bool is_full() { return fused_refs==threshold; } + /// filled to threshold (THRESHOLD_PERCENT=75), needs expanding + bool is_full() { return fused_refs + allocated/4 >= allocated; } /// allocate larger buffer & rehash void expand() { int old_allocated=allocated; Pair **old_refs=refs; - allocates_index=allocates_index+1fused_refs++; // not, we'll use it and record the fact - NEW_PAIR(code, key, value); + HASH_NEW_PAIR(code, key, value); this->fpairs_count++; return false; } - /// put a [value] under the [key] @returns existed or not - template R replace_maybe_append(K str, V value, F prevent, I info) { - if(!value) { - // they can come here from somewhere (true with maybe_replace_maybe_append, keeping parallel) - remove(str); - // this has nothing to do with properties, doing no special property handling here - return 0; - } - - if(this->is_full()) - this->expand(); - - CORD key=str.get_cord(); - - uint code=str.get_hash_code(); - uint index=code%this->allocated; - Pair **ref=&this->refs[index]; - for(Pair *pair=*ref; pair; pair=pair->link) - if(pair->code==code && CORD_cmp(pair->key,key)==0) { - // found a pair with the same key - pair->value=value; - return reinterpret_cast(1); - } - - // proper pair not found - // prevent-function intercepted append? - if(R result=prevent(value, info)) - return result; - - //create&link_in new pair - if(!*ref) // root cell were fused_refs? - this->fused_refs++; // not, we'll use it and record the fact - NEW_PAIR(code, key, value); - this->fpairs_count++; - return 0; - } - - /// put a [value] under the [key] @returns existed or not - template - R maybe_replace_maybe_append(K str, V value, F1 prevent_replace, F2 prevent_append, I info) { - if(!value) { - // they can come here from Temp_value_element::dctor to restore some empty value - remove(str); - // this has nothing to do with properties, doing no special property handling here - return 0; - } - - if(this->is_full()) - this->expand(); - - CORD key=str.get_cord(); - - uint code=str.get_hash_code(); - uint index=code%this->allocated; - Pair **ref=&this->refs[index]; - for(Pair *pair=*ref; pair; pair=pair->link) - if(pair->code==code && CORD_cmp(pair->key,key)==0) { - // found a pair with the same key - - // prevent-function intercepted replace? - if(R result=prevent_replace(pair->value, info)) - return result; - - pair->value=value; - return reinterpret_cast(1); - } - - // proper pair not found - // prevent-function intercepted append? - if(R result=prevent_append(value, info)) - return result; - - //create&link_in new pair - if(!*ref) // root cell were fused_refs? - this->fused_refs++; // not, we'll use it and record the fact - NEW_PAIR(code, key, value); - this->fpairs_count++; - return 0; - } - - /// put a [value] under the [key] @returns existed or not - template - R maybe_replace_never_append(K str, V value, F1 prevent_replace, I info) { - if(!value) { - // they can come here from somewhere (true with maybe_replace_maybe_append, keeping parallel) - remove(str); - // this has nothing to do with properties, doing no special property handling here - return 0; - } - - if(this->is_full()) - this->expand(); - - CORD key=str.get_cord(); - - uint code=str.get_hash_code(); - uint index=code%this->allocated; - Pair **ref=&this->refs[index]; - for(Pair *pair=*ref; pair; pair=pair->link) - if(pair->code==code && CORD_cmp(pair->key,key)==0) { - // found a pair with the same key - - // prevent-function intercepted replace? - if(R result=prevent_replace(pair->value, info)) - return result; - - pair->value=value; - return reinterpret_cast(1); - } - - return 0; - - } - /// remove the [key] @returns existed or not bool remove(K str) { CORD key=str.get_cord(); @@ -733,7 +508,6 @@ public: else this->last=pair->prev; #endif - delete pair; *ref=next; --this->fpairs_count; return true; @@ -768,34 +542,27 @@ public: return V(0); } - /// put a [value] under the [key] if that [key] existed @returns existed or not - bool put_replaced(K str, V value) { - if(!value) { - remove(str); - return false; + /// get associated [value] by the [key], optimized + V get(const char *key) const { + uint code=0; + if(key && *key){ + generic_hash_code(code, key); + } else { + key=0; } - - CORD key=str.get_cord(); - uint code=str.get_hash_code(); uint index=code%this->allocated; for(Pair *pair=this->refs[index]; pair; pair=pair->link) - if(pair->code==code && CORD_cmp(pair->key,key)==0) { - // found a pair with the same key, replacing - pair->value=value; - return true; - } + if(pair->code==code && CORD_cmp(pair->key,(CORD)key)==0) + return pair->value; - // proper pair not found - return false; + return V(0); } /// put a [value] under the [key] if that [key] existed @returns existed or not - template R maybe_put_replaced(K str, V value, F prevent) { + bool put_replaced(K str, V value) { if(!value) { - // they can come here from Temp_value_element::dctor to restore some empty value remove(str); - // this has nothing to do with properties, doing no special property handling here - return 0; + return false; } CORD key=str.get_cord(); @@ -804,16 +571,12 @@ public: for(Pair *pair=this->refs[index]; pair; pair=pair->link) if(pair->code==code && CORD_cmp(pair->key,key)==0) { // found a pair with the same key, replacing - // prevent-function intercepted put? - if(R result=prevent(pair->value)) - return result; - pair->value=value; - return reinterpret_cast(1); + return true; } // proper pair not found - return 0; + return false; } /// put a [value] under the [key] if that [key] NOT existed @returns existed or not @@ -838,75 +601,159 @@ public: // proper pair not found -- create&link_in new pair if(!*ref) // root cell were fused_refs? this->fused_refs++; // not, we'll use it and record the fact - NEW_PAIR(code, key, value); + HASH_NEW_PAIR(code, key, value); this->fpairs_count++; return false; } - /// iterate over all pairs - template void for_each(void callback(K, V, I), I info) const { + /// put all 'src' values if NO with same key existed + void merge_dont_replace(const HASH_STRING& src) { #ifdef HASH_ORDER - for(Pair *pair=this->first; pair; pair=pair->next) - callback(pair->key, pair->value, info); + for(Pair *pair=src.first; pair; pair=pair->next) #else - Pair **ref=this->refs; - for(int index=0; indexallocated; index++) - for(Pair *pair=*ref++; pair; pair=pair->link) - callback(String::Body(pair->key, pair->code), pair->value, info); + for(int i=0; ilink) #endif + put_dont_replace(String::Body(pair->key, pair->code), pair->value); + } + + /// iterate over all pairs + template void for_each(void callback(K, V, I), I info) const { + HASH_FOR_EACH + callback(String::Body(pair->key, pair->code), pair->value, info); } /// iterate over all pairs template void for_each_ref(void callback(K, V&, I), I info) const { -#ifdef HASH_ORDER - for(Pair *pair=this->first; pair; pair=pair->next) - callback(pair->key, pair->value, info); -#else - Pair **ref=this->refs; - for(int index=0; indexallocated; index++) - for(Pair *pair=*ref++; pair; pair=pair->link) - callback(String::Body(pair->key, pair->code), pair->value, info); -#endif + HASH_FOR_EACH + callback(String::Body(pair->key, pair->code), pair->value, info); } /// iterate over all pairs until condition becomes true, return that element template V first_that(bool callback(K, V, I), I info) const { -#ifdef HASH_ORDER - for(Pair *pair=this->first; pair; pair=pair->next) + HASH_FOR_EACH if(callback(String::Body(pair->key, pair->code), pair->value, info)) return pair->value; -#else - Pair **ref=this->refs; - for(int index=0; indexallocated; index++) - for(Pair *pair=*ref++; pair; pair=pair->link) - if(callback(String::Body(pair->key, pair->code), pair->value, info)) - return pair->value; -#endif return V(0); } -}; + #else //HASH_CODE_CACHING -template class HASH_STRING: public HASH{}; +template class HASH_STRING: public HASH{ +public: + typedef typename HASH::Pair Pair; #endif //HASH_CODE_CACHING + /// simple hash iterator + class Iterator { + const HASH_STRING& fhash; + Pair *fcurrent; +#ifndef HASH_ORDER + int i; +#endif + public: +#ifdef HASH_ORDER + Iterator(const HASH_STRING& ahash): fhash(ahash) { + fcurrent=fhash.first; + } + + void next() { + fcurrent=fcurrent->next; + } +#else + Iterator(const HASH_STRING& ahash): fhash(ahash) { + fcurrent=0; + for(i=0; ilink) + return; + for(i++; ikey, fcurrent->code); +#else + return fcurrent->key; +#endif + } + + V value(){ + return fcurrent->value; + } + + Pair *pair(){ + return fcurrent; + } + }; + +#ifdef HASH_ORDER + /// simple reverse hash iterator + class ReverseIterator { + const HASH_STRING& fhash; + Pair *fcurrent; + public: + ReverseIterator(const HASH_STRING& ahash): fhash(ahash) { + fcurrent=fhash.last_pair(); + } + + void prev() { + fcurrent=(fcurrent->prev == &fhash.first) ? NULL : (Pair*)((char *)fcurrent->prev - offsetof(Pair, next)); + } + + operator bool () { + return fcurrent != NULL; + } + + String::Body key(){ +#ifdef HASH_CODE_CACHING + return String::Body(fcurrent->key, fcurrent->code); +#else + return fcurrent->key; +#endif + } + + V value(){ + return fcurrent->value; + } + + Pair *pair(){ + return fcurrent; + } + }; +#endif + +}; + #ifndef HASH_ORDER /// Auto-object used to temporarily substituting/removing string hash values -template +template class Temp_hash_value { - HashString &fhash; - K fname; + H *fhash; + String::Body fname; V saved_value; public: - Temp_hash_value(HashString& ahash, K aname, V avalue) : - fhash(ahash), - fname(aname), - saved_value(ahash.get(aname)) { - fhash.put(aname, avalue); + Temp_hash_value(H *ahash, String::Body aname, V avalue) : fhash(ahash), fname(aname) { + if(fhash){ + saved_value=fhash->get(aname); + fhash->put(aname, avalue); + } } - ~Temp_hash_value() { - fhash.put(fname, saved_value); + ~Temp_hash_value() { + if(fhash) + fhash->put(fname, saved_value); } }; #endif