From 367940dd974193470df4d3453ddcc2030b06bc0e Mon Sep 17 00:00:00 2001 From: guy Date: Mon, 21 Dec 2015 11:22:35 +0000 Subject: [PATCH] git-svn-id: svn://10.65.10.50/branches/R_10_00@23164 c028cbd2-c16b-5b4b-a496-9718f37d4682 --- include/assoc.cpp | 28 ++--- include/assoc.h | 6 +- include/hashtable.cpp | 279 ++++++++++++++++++++++++++++++++++++++++++ include/hashtable.h | 47 +++++++ include/relapp.cpp | 57 +++++++-- include/relapp.h | 1 + include/relation.h | 6 +- 7 files changed, 391 insertions(+), 33 deletions(-) create mode 100644 include/hashtable.cpp create mode 100644 include/hashtable.h diff --git a/include/assoc.cpp b/include/assoc.cpp index 43757d7a3..6d7b150d0 100755 --- a/include/assoc.cpp +++ b/include/assoc.cpp @@ -42,7 +42,7 @@ THash_object* TAssoc_array::_lookup( const word hv = key.hash() % HASH_SIZE; TArray& arr = bucket(hv); THash_object* o = NULL; - isnew = FALSE; + isnew = false; int i; for (i = 0; i < arr.items(); i++) @@ -62,7 +62,7 @@ THash_object* TAssoc_array::_lookup( arr.insert(o,i); _cnt++; } - isnew = TRUE; + isnew = true; } return o; @@ -190,10 +190,7 @@ bool TAssoc_array::add( o->_obj = obj; } else - { - NFCHECK("deleting duplicate hash object"); delete obj; - } return true; } o->_obj = obj; @@ -212,19 +209,18 @@ bool TAssoc_array::add(const char* key, const TObject& obj, bool force) */ bool isnew = false; THash_object* o = _lookup(key, isnew, true); - if (!isnew) + if (isnew) { - if (force) - { - if (o->_obj != NULL) - delete o->_obj; - o->_obj = obj.dup(); - } - return true; + o->_obj = obj.dup(); + return false; } - o->_obj = obj.dup(); - return false; - + if (force) + { + if (o->_obj != NULL) + delete o->_obj; + o->_obj = obj.dup(); + } + return true; } // @doc EXTERNAL diff --git a/include/assoc.h b/include/assoc.h index d63cbd336..4d2861174 100755 --- a/include/assoc.h +++ b/include/assoc.h @@ -15,9 +15,7 @@ // // @base public | TObject class THash_object : public TObject - -// @author:(INTERNAL) Guido - +// @author:(INTERNAL) Villa { // @cfriend TAssoc_array friend class TAssoc_array; @@ -55,7 +53,7 @@ public: // @base public |TObject class TAssoc_array : public TContainer -// @author:(INTERNAL) Guido +// @author:(INTERNAL) Villa //@access:(INTERNAL) Private Member { diff --git a/include/hashtable.cpp b/include/hashtable.cpp new file mode 100644 index 000000000..d1766a97a --- /dev/null +++ b/include/hashtable.cpp @@ -0,0 +1,279 @@ +#include + +#define STRICT +#define WIN32_LEAN_AND_MEAN +#define VC_EXTRALEAN +#include +#include + +struct THash_table::THashEntry +{ + char* key; + unsigned int hash; + TObject* value; + THashEntry* next; + + THashEntry(const char* key, unsigned int hash, TObject* value); + ~THashEntry() + { + if (key) free(key); + if (value) delete value; + } +}; + +/////////////////////////////////////////////////////////// +// Utilities +/////////////////////////////////////////////////////////// + +static inline unsigned int ELFHash(const char* str) +{ + unsigned int hash = 0; + unsigned int x = 0; + for(const char* c = str; *c; c++) + { + hash = (hash << 4) + *c; + x = hash & 0xF0000000L; + if (x != 0) + { + hash ^= (x >> 24); + hash &= ~x; + } + } + return (hash & 0x7FFFFFFF); +} + +static inline size_t calculateIndex(unsigned int bucketCount, unsigned int hash) +{ return ((size_t)hash) & (bucketCount - 1); } + +/////////////////////////////////////////////////////////// +// THash_table +/////////////////////////////////////////////////////////// + +void THash_table::expand_if_needed() +{ + // If the load factor exceeds 0.5... + if (_size > (_bucketCount / 2)) + { + const size_t newBucketCount = _bucketCount * 2; + THashEntry** newBuckets = (THashEntry**)calloc(newBucketCount, sizeof(THashEntry*)); + if (newBuckets) + { + // Move over existing entries. + for (size_t i = 0; i < _bucketCount; i++) + { + THashEntry* entry = _buckets[i]; + while (entry != NULL) + { + THash_table::THashEntry* next = entry->next; + const size_t index = ::calculateIndex(newBucketCount, entry->hash); + entry->next = newBuckets[index]; + newBuckets[index] = entry; + entry = next; + } + } + // Copy over internals: _size remains untouched + free(_buckets); + _buckets = newBuckets; + _bucketCount = newBucketCount; + } + } +} + +void THash_table::Lock() +{ + while (::InterlockedCompareExchange(&_lock, 1, 0)) + ::Sleep(0); +} + +void THash_table::Unlock() +{ + ::InterlockedDecrement(&_lock); +} + +THash_table::~THash_table() +{ + Lock(); + for (size_t i = 0; i < _bucketCount; i++) + { + THashEntry* entry = _buckets[i]; + while (entry != NULL) + { + THash_table::THashEntry* next = entry->next; + delete entry; + entry = next; + } + } + ::free(_buckets); +} + + +THash_table::THashEntry::THashEntry(const char* str, unsigned int h, TObject* val) +{ + CHECK(str && *str, "NULL hash key"); + key = _strdup(str); + hash = h; + value = val; + next = NULL; +} + +THash_table::THashEntry* THash_table::lookup_ex(const char* key, unsigned int& hash, size_t& index) const +{ + hash = ELFHash(key); + index = calculateIndex(_bucketCount, hash); + THashEntry* entry = _buckets[index]; + for (; entry; entry = entry->next) + { + if (entry->hash == hash && strcmp(entry->key, key)==0) + break; + } + return entry; +} + +THash_table::THashEntry* THash_table::lookup(const char* key) const +{ + unsigned int hash; + size_t index; + return lookup_ex(key, hash, index); +} + +bool THash_table::add(const char* key, TObject* value, bool force) +{ + Lock(); + unsigned int hash; + size_t index; + THashEntry* current = lookup_ex(key, hash, index); + if (current) + { + if (force) + { + delete current->value; + current->value = value; + } + else + delete value; + } + else + { + THashEntry* new_entry = new THashEntry(key, hash, value); + new_entry->next = _buckets[index]; + _buckets[index] = new_entry; + _size++; + } + Unlock(); + return current != NULL; +} + +bool THash_table::add(const char* key, const TObject& value, bool force) +{ + Lock(); + unsigned int hash; + size_t index; + THashEntry* current = lookup_ex(key, hash, index); + if (current) + { + if (force) + { + delete current->value; + current->value = value.dup(); + } + } + else + { + THashEntry* new_entry = new THashEntry(key, hash, value.dup()); + new_entry->next = _buckets[index]; + _buckets[index] = new_entry; + _size++; + } + Unlock(); + return current != NULL; +} + +TObject* THash_table::get(const char* key) +{ + THashEntry* entry = lookup(key); + return entry ? entry->value : NULL; +} + +TObject* THash_table::remove(const char* key) +{ + Lock(); + unsigned int hash; + size_t index; + THash_table::THashEntry* e = lookup_ex(key, hash, index); + if (e) + { + TObject* value = NULL; + // Pointer to the current THashEntry. + THashEntry** p = &(_buckets[index]); + THashEntry* current; + while ((current = *p) != NULL) + { + if (current == e) + { + value = current->value; + current->value = NULL; + p = ¤t->next; + delete current; + _size--; + break; + } + p = ¤t->next; + } + } + Unlock(); + return e ? e->value : NULL; +} + +bool THash_table::destroy(const char* key) +{ + TObject* o = remove(key); + if (o) delete o; + return o != NULL; +} + +THash_table::THash_table(size_t initialCapacity) +{ + _bucketCount = 8; + // Bucket count must be power of 2. + while (_bucketCount <= initialCapacity) + _bucketCount *= 2; + _buckets = (THashEntry**)calloc(_bucketCount, sizeof(THashEntry*)); + assert(_buckets != NULL); + + _size = 0; + _lock = 0; + + _curr_bucket = 0; + _curr_entry = NULL; +} + +TObject* THash_table::succ_item() +{ + for (;;) + { + while (_curr_entry) + { + TObject* obj = _curr_entry->value; + _curr_entry = _curr_entry->next; + if (obj) + return obj; + } + if (++_curr_bucket >= _bucketCount) + break; + _curr_entry = _buckets[_curr_bucket]; + } + return NULL; +} + +TObject* THash_table::first_item() +{ + _curr_bucket = -1; + _curr_entry = NULL; + return succ_item(); +} + +TObject* THash_table::last_item() +{ return first_item(); } + +TObject* THash_table::pred_item() +{ return succ_item(); } diff --git a/include/hashtable.h b/include/hashtable.h new file mode 100644 index 000000000..44752ac0d --- /dev/null +++ b/include/hashtable.h @@ -0,0 +1,47 @@ +#pragma once +#ifndef __HASHTABLE_H__ +#define __HASHTABLE_H__ + +#ifndef __ARRAY_H__ +#include +#endif + +class THash_table : public TContainer +{ + struct THashEntry; + THashEntry** _buckets; + size_t _bucketCount, _size; + volatile unsigned int _lock; + + // container iterator + size_t _curr_bucket; + THashEntry* _curr_entry; + +protected: + THashEntry* lookup_ex(const char* key, unsigned int& hash, size_t& index) const; + THashEntry* lookup(const char* key) const; + void expand_if_needed(); + void Lock(); + void Unlock(); + +public: // Container interface + virtual TObject* first_item(); + virtual TObject* last_item(); + virtual TObject* succ_item(); + virtual TObject* pred_item(); + virtual long objects() const { return _size; } + +public: + bool add(const char* key, TObject* value, bool force = false); + bool add(const char* key, const TObject& value, bool force = false); + bool is_key(const char* key) const { return lookup(key) != NULL; } + TObject* get(const char* key); + TObject* remove(const char* key); + bool destroy(const char* key); + size_t items() const { return _size; } + + THash_table(size_t sz = 0); + ~THash_table(); +}; + +#endif diff --git a/include/relapp.cpp b/include/relapp.cpp index 7fe764e7a..d9e861cba 100755 --- a/include/relapp.cpp +++ b/include/relapp.cpp @@ -1003,9 +1003,46 @@ void TRelation_application::edit_cancel() } } +short TRelation_application::mask_field_dirty() const +{ + short id = 0; + if (_mask) + { + const int mode = _mask->mode(); + if (mode == MODE_QUERY) + { + FOR_EACH_MASK_FIELD(*_mask, i, f) + { + if (f->dirty() && f->active()) + { + id = f->dlg(); + break; + } + } + } + else + { + FOR_EACH_MASK_FIELD(*_mask, i, f) + { + if (f->dirty() && f->active() && f->field()) + { + if (id == 0) + id = f->dlg(); + if (f->dirty() > 1) // error on field content + { + id = f->dlg(); + break; + } + } + } + } + } + return id; +} + bool TRelation_application::save(bool check_dirty) { - static bool was_dirty = FALSE; + static bool was_dirty = false; int pos = _mask->id2pos(DLG_SAVEREC); if (pos < 0 || !_mask->fld(pos).active()) @@ -1018,7 +1055,7 @@ bool TRelation_application::save(bool check_dirty) const int mode = _mask->mode(); if (check_dirty) { - const short dirty = _mask->dirty(); + const short dirty = mask_field_dirty(); if (mode == MODE_QUERY) { @@ -1090,26 +1127,26 @@ bool TRelation_application::save(bool check_dirty) if (!ff.on_key(K_TAB)) { _mask->first_focus(-ff.dlg()); - was_dirty = TRUE; - return FALSE; + was_dirty = true; + return false; } } - if (!_mask->check_fields()) // Exit with ESC didn't check values + if (!_mask->check_fields()) // Exit with ESC didn't check values { // check_fields sets the focus on the blocking field _mask->first_focus(-_mask->focus_field().dlg()); - was_dirty = TRUE; - return FALSE; + was_dirty = true; + return false; } } } - was_dirty = FALSE; + was_dirty = false; TWait_cursor hourglass; if (mode == MODE_INS) { - bool changed = TRUE; - bool changed_key = FALSE; + bool changed = true; + bool changed_key = false; while (changed) { diff --git a/include/relapp.h b/include/relapp.h index 4427b8d0e..4b0e08049 100755 --- a/include/relapp.h +++ b/include/relapp.h @@ -110,6 +110,7 @@ private: // @cmember:(INTERNAL) Sistema il bottone ricerca se necessario void set_find_button(); + short mask_field_dirty() const; // @access Protected Member protected: // TApplication diff --git a/include/relation.h b/include/relation.h index 32b3e8a89..8a1732ab6 100755 --- a/include/relation.h +++ b/include/relation.h @@ -132,11 +132,11 @@ public: // TObject void replacef(TLocalisamfile* f, int lognum = 0,const char * relexprs="",int key=1); // @cmember Aggiunge il record corrente - virtual int write (bool force = TRUE); + virtual int write (bool force = true); // @cmember Riscrive il record corrente - virtual int rewrite (bool force = TRUE); + virtual int rewrite (bool force = true); // @cmember Elimina il record corrente - virtual int remove (); + virtual int remove(); // @cmember Controlla se e' stata raggiunta la fine del file // (se

= 0 dell'intera relazione, altrimenti del file indicato )