git-svn-id: svn://10.65.10.50/branches/R_10_00@23164 c028cbd2-c16b-5b4b-a496-9718f37d4682
This commit is contained in:
parent
501a4cf167
commit
367940dd97
@ -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
|
||||
|
@ -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
|
||||
{
|
||||
|
279
include/hashtable.cpp
Normal file
279
include/hashtable.cpp
Normal file
@ -0,0 +1,279 @@
|
||||
#include <hashtable.h>
|
||||
|
||||
#define STRICT
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#define VC_EXTRALEAN
|
||||
#include <windows.h>
|
||||
#include <cassert>
|
||||
|
||||
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(); }
|
47
include/hashtable.h
Normal file
47
include/hashtable.h
Normal file
@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
#ifndef __HASHTABLE_H__
|
||||
#define __HASHTABLE_H__
|
||||
|
||||
#ifndef __ARRAY_H__
|
||||
#include <array.h>
|
||||
#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
|
@ -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)
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -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 <p logicnum> = 0 dell'intera relazione, altrimenti del file indicato )
|
||||
|
Loading…
x
Reference in New Issue
Block a user