--- parser3/src/include/pa_hash.h 2009/04/17 13:13:09 1.71 +++ parser3/src/include/pa_hash.h 2009/07/29 05:01:46 1.78 @@ -1,7 +1,7 @@ /** @file Parser: hash class decl. - Copyright (c) 2001-2005 ArtLebedev Group (http://www.artlebedev.com) + Copyright (c) 2001-2009 ArtLebedev Group (http://www.artlebedev.com) Author: Alexandr Petrosian (http://paf.design.ru) */ @@ -17,10 +17,11 @@ #ifndef PA_HASH_H #define PA_HASH_H -static const char * const IDENT_HASH_H="$Date: 2009/04/17 13:13:09 $"; +static const char * const IDENT_HASH_H="$Date: 2009/07/29 05:01:46 $"; #include "pa_memory.h" #include "pa_types.h" +#include "pa_string.h" const int HASH_ALLOCATES_COUNT=29; @@ -72,6 +73,10 @@ inline uint hash_code(int self) { return result; } +#endif // PA_HASH_H + +#ifndef PA_HASH_CLASS +#define PA_HASH_CLASS /** Simple hash. @@ -80,43 +85,77 @@ inline uint hash_code(int self) { get returning 0 means there were no such. "put value 0" means "remove" */ -template class Hash: public PA_Object { +#ifdef HASH_ORDER + +#undef HASH +#undef HASH_STRING +#undef NEW_PAIR + +#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) + +#else + +#define HASH Hash +#define HASH_STRING HashString +#define NEW_PAIR(code, key, value) *ref=new Pair(code, key, value, *ref) + +#endif + +template class HASH: public PA_Object { public: typedef K key_type; typedef V value_type; - Hash() { + 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 } - Hash(const Hash& source) { + 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 // clone & rehash Pair **old_ref=source.refs; for(int index=0; indexlink; - Pair **new_ref=&refs[index]; - *new_ref=new Pair(pair->code, pair->key, pair->value, *new_ref); + Pair **ref=&refs[index]; + NEW_PAIR(pair->code, pair->key, pair->value); pair=next; } } - ~Hash() { +#ifdef USE_DESTRUCTORS + ~HASH() { + Pair **ref=refs; + for(int index=0; indexlink; + delete pair; + pair=next; + } delete[] refs; } +#endif /// put a [value] under the [key] @returns existed or not bool put(K key, V value) { @@ -140,7 +179,7 @@ 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 - *ref=new Pair(code, key, value, *ref); + NEW_PAIR(code, key, value); fpairs_count++; return false; } @@ -175,7 +214,7 @@ public: //create&link_in new pair if(!*ref) // root cell were fused_refs? fused_refs++; // not, we'll use it and record the fact - *ref=new Pair(code, key, value, *ref); + NEW_PAIR(code, key, value); fpairs_count++; return 0; } @@ -217,7 +256,7 @@ public: //create&link_in new pair if(!*ref) // root cell were fused_refs? fused_refs++; // not, we'll use it and record the fact - *ref=new Pair(code, key, value, *ref); + NEW_PAIR(code, key, value); fpairs_count++; return 0; } @@ -258,15 +297,24 @@ public: bool remove(K key) { uint code=hash_code(key); uint index=code%allocated; - for(Pair **ref=&refs[index]; *ref; ref=&(*ref)->link) - if((*ref)->code==code && (*ref)->key==key) { + for(Pair **ref=&refs[index]; *ref; ref=&(*ref)->link){ + Pair *pair=*ref; + if(pair->code==code && pair->key==key) { // found a pair with the same key - Pair *next=(*ref)->link; - delete *ref; + Pair *next=pair->link; +#ifdef HASH_ORDER + *(pair->prev)=pair->next; + if(pair->next) + pair->next->prev=pair->prev; + else + last=pair->prev; +#endif + delete pair; *ref=next; --fpairs_count; return true; } + } return false; } @@ -294,16 +342,6 @@ public: return V(0); } - /// get associated [value] by the [key] + [code] (faster) - V get_by_hash_code(uint code, K key) const { - uint index=code%allocated; - for(Pair *pair=refs[index]; pair; pair=pair->link) - if(pair->code==code && pair->key==key) - return pair->value; - - 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) { @@ -370,7 +408,7 @@ 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 - *ref=new Pair(code, key, value, *ref); + NEW_PAIR(code, key, value); fpairs_count++; return false; } @@ -378,7 +416,7 @@ public: /** put all 'src' values if NO with same key existed @todo optimize this.allocated==src.allocated case */ - void merge_dont_replace(const Hash& src) { + void merge_dont_replace(const HASH& src) { for(int i=0; ilink) put_dont_replace(pair->key, pair->value); @@ -389,28 +427,43 @@ 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) + 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) + 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) + 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); } @@ -418,9 +471,13 @@ public: void clear() { memset(refs, 0, sizeof(*refs)*allocated); fpairs_count=fused_refs=0; +#ifdef HASH_ORDER + first=0; + last=&first; +#endif } -private: +protected: /// expand when these %% of allocated exausted enum { @@ -449,14 +506,22 @@ private: K key; V value; Pair *link; - - Pair(uint acode, K akey, V avalue, Pair *alink) : - code(acode), - key(akey), - value(avalue), - link(alink) {} +#ifdef HASH_ORDER + Pair **prev; + Pair *next; + + Pair(uint acode, K akey, V avalue, Pair *alink, Pair **aprev) : code(acode), key(akey), value(avalue), link(alink), + prev(aprev), next(0) { *aprev=this; } +#else + Pair(uint acode, K akey, V avalue, Pair *alink) : code(acode), key(akey), value(avalue), link(alink) {} +#endif } **refs; +#ifdef HASH_ORDER + Pair *first; + Pair **last; +#endif + /// filled to threshold: needs expanding bool is_full() { return fused_refs==threshold; } @@ -490,17 +555,351 @@ private: private: //disabled - Hash& operator = (const Hash&) { return *this; } + HASH& operator = (const HASH&) { return *this; } +}; + +/** + Simple String::body hash. + Allows hash code caching +*/ + +#ifdef HASH_CODE_CACHING + +template class HASH_STRING: public HASH { +public: + + typedef typename HASH::Pair Pair; + typedef const String::Body &K; + + typedef K key_type; + + /// put a [value] under the [key] @returns existed or not + bool put(K str, V value) { + if(!value) { + remove(str); + return false; + } + 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 true; + } + + // 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); + 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(); + uint code=str.get_hash_code(); + uint index=code%this->allocated; + for(Pair **ref=&this->refs[index]; *ref; ref=&(*ref)->link){ + Pair *pair=*ref; + if(pair->code==code && CORD_cmp(pair->key,key)==0) { + // found a pair with the same key + Pair *next=pair->link; +#ifdef HASH_ORDER + *(pair->prev)=pair->next; + if(pair->next) + pair->next->prev=pair->prev; + else + this->last=pair->prev; +#endif + delete pair; + *ref=next; + --this->fpairs_count; + return true; + } + } + + return false; + } + + /// return true if key exists + bool contains(K str){ + 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) + return true; + } + + return false; + } + + /// get associated [value] by the [key] + V get(K str) const { + 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) + return pair->value; + + 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; + } + + 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; + } + + // proper pair not found + return false; + } + + /// 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) { + 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; + } + + 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 + // prevent-function intercepted put? + if(R result=prevent(pair->value)) + return result; + + pair->value=value; + return reinterpret_cast(1); + } + + // proper pair not found + return 0; + } + + /// put a [value] under the [key] if that [key] NOT existed @returns existed or not + bool put_dont_replace(K str, V value) { + if(!value) { + remove(str); + return false; + } + 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, NOT replacing + return true; + } + + // 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); + this->fpairs_count++; + return false; + } + + /// iterate over all pairs + template void for_each(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 + } + + /// 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 + } + + /// 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) + 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{}; -/// Auto-object used to temporarily substituting/removing hash values +#endif //HASH_CODE_CACHING + +#ifndef HASH_ORDER +/// Auto-object used to temporarily substituting/removing string hash values template class Temp_hash_value { - Hash& fhash; + HashString &fhash; K fname; V saved_value; public: - Temp_hash_value(Hash& ahash, K aname, V avalue) : + Temp_hash_value(HashString& ahash, K aname, V avalue) : fhash(ahash), fname(aname), saved_value(ahash.get(aname)) { @@ -510,5 +909,6 @@ public: fhash.put(fname, saved_value); } }; - #endif + +#endif //PA_HASH_CLASS