--- parser3/src/include/pa_inline_hash.h 2026/04/25 02:28:31 1.2 +++ parser3/src/include/pa_inline_hash.h 2026/05/28 14:29:56 1.8 @@ -1,62 +1,65 @@ /** @file Parser: inline-storage hash decls. - Copyright (c) 2001-2026 Art. Lebedev Studio (http://www.artlebedev.com) + Copyright (c) 2001-2026 Art. Lebedev Studio (https://www.artlebedev.com) Authors: Konstantin Morshnev */ #ifndef PA_INLINE_HASH_H #define PA_INLINE_HASH_H -#define IDENT_PA_INLINE_HASH_H "$Id: pa_inline_hash.h,v 1.2 2026/04/25 02:28:31 moko Exp $" +#define IDENT_PA_INLINE_HASH_H "$Id: pa_inline_hash.h,v 1.8 2026/05/28 14:29:56 moko Exp $" #include "pa_hash.h" -#define PA_HASH_INLINE_SIZE 4 +#define PA_INLINE_HASH_N 13 // Low overflow rate, high first-hit ratio +#define PA_PROBE_LIMIT 8 // Prevents long probe chains from degrading the inline table -/// Inline-storage "hash" stores up to PA_HASH_INLINE_SIZE entries without heap allocation. -/// Fallbacks to HashString for overflow. +// Linear-probing inline hash with PA_INLINE_HASH_N slots before overflow to HashString. template class InlineHashString: public PA_Object { public: - struct Pair { - String::Body key; - V value; - }; - - InlineHashString() : fcount(0), foverflow(0) {} + InlineHashString() : foverflow(0) { + memset(fkeys, 0, sizeof(fkeys)); + } + ~InlineHashString() { #ifdef USE_DESTRUCTORS - ~InlineHashString() { if(foverflow) delete foverflow; } + if(foverflow) delete foverflow; #endif + } V get(const String& name) const { const String::Body& nb = name; - const uint code = nb.get_hash_code(); - for(int i = 0; i < fcount; i++) { - const String::Body& key = fpairs[i].key; - if(key.get_hash_code() == code && key == nb) - return fpairs[i].value; + const uint hash = nb.get_hash_code(); + int i = hash % PA_INLINE_HASH_N; + + // Optimized first, >50% hits + if(!fkeys[i]) return 0; // NULL implies foverflow==NULL + if(fkeys[i]->get_hash_code() == hash && *fkeys[i] == nb) return fvalues[i]; + // Сollision + for(int n = 0; n < PA_PROBE_LIMIT-1; n++) { + if(++i >= PA_INLINE_HASH_N) i = 0; + if(!fkeys[i]) return 0; + if(fkeys[i]->get_hash_code() == hash && *fkeys[i] == nb) return fvalues[i]; } return foverflow ? foverflow->get(name) : 0; } bool put(const String& name, V value) { const String::Body& nb = name; - const uint code = nb.get_hash_code(); - for(int i = 0; i < fcount; i++) { - const String::Body& key = fpairs[i].key; - if(key.get_hash_code() == code && key == nb) { - fpairs[i].value = value; - return true; - } - } - if(fcount < PA_HASH_INLINE_SIZE) { - fpairs[fcount].key = name; - fpairs[fcount].value = value; - fcount++; - return false; + const uint hash = nb.get_hash_code(); + int i = hash % PA_INLINE_HASH_N; + + // Optimized first + if(!fkeys[i]) { fkeys[i] = &nb; fvalues[i] = value; return false; } + if(fkeys[i]->get_hash_code() == hash && *fkeys[i] == nb) { fvalues[i] = value; return true; } + // Сollision + for(int n = 0; n < PA_PROBE_LIMIT-1; n++) { + if(++i >= PA_INLINE_HASH_N) i = 0; + if(!fkeys[i]) { fkeys[i] = &nb; fvalues[i] = value; return false; } + if(fkeys[i]->get_hash_code() == hash && *fkeys[i] == nb) { fvalues[i] = value; return true; } } if(!foverflow) foverflow = new HashString(); @@ -65,32 +68,35 @@ public: bool put_replaced(const String& name, V value) { const String::Body& nb = name; - const uint code = nb.get_hash_code(); - for(int i = 0; i < fcount; i++) { - const String::Body& key = fpairs[i].key; - if(key.get_hash_code() == code && key == nb) { - fpairs[i].value = value; - return true; - } + const uint hash = nb.get_hash_code(); + int i = hash % PA_INLINE_HASH_N; + + // Optimized first + if(!fkeys[i]) return false; + if(fkeys[i]->get_hash_code() == hash && *fkeys[i] == nb) { fvalues[i] = value; return true; } + // Сollision + for(int n = 0; n < PA_PROBE_LIMIT-1; n++) { + if(++i >= PA_INLINE_HASH_N) i = 0; + if(!fkeys[i]) return false; + if(fkeys[i]->get_hash_code() == hash && *fkeys[i] == nb) { fvalues[i] = value; return true; } } return foverflow ? foverflow->put_replaced(name, value) : false; } - int count() const { - return fcount + (foverflow ? foverflow->count() : 0); - } - template void for_each(void callback(const String::Body&, V, I), I info) const { - for(int i = 0; i < fcount; i++) - callback(fpairs[i].key, fpairs[i].value, info); + for(int i = 0; i < PA_INLINE_HASH_N; i++) { + if(fkeys[i]) + callback(*fkeys[i], fvalues[i], info); + } if(foverflow) foverflow->for_each(callback, info); } private: - int fcount; - Pair fpairs[PA_HASH_INLINE_SIZE]; HashString* foverflow; + const String::Body* fkeys[PA_INLINE_HASH_N]; // NULL=empty, initialized in constructor + V fvalues[PA_INLINE_HASH_N]; + }; #endif // PA_INLINE_HASH_H