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;
|
const word hv = key.hash() % HASH_SIZE;
|
||||||
TArray& arr = bucket(hv);
|
TArray& arr = bucket(hv);
|
||||||
THash_object* o = NULL;
|
THash_object* o = NULL;
|
||||||
isnew = FALSE;
|
isnew = false;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < arr.items(); i++)
|
for (i = 0; i < arr.items(); i++)
|
||||||
@ -62,7 +62,7 @@ THash_object* TAssoc_array::_lookup(
|
|||||||
arr.insert(o,i);
|
arr.insert(o,i);
|
||||||
_cnt++;
|
_cnt++;
|
||||||
}
|
}
|
||||||
isnew = TRUE;
|
isnew = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return o;
|
return o;
|
||||||
@ -190,10 +190,7 @@ bool TAssoc_array::add(
|
|||||||
o->_obj = obj;
|
o->_obj = obj;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
|
||||||
NFCHECK("deleting duplicate hash object");
|
|
||||||
delete obj;
|
delete obj;
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
o->_obj = obj;
|
o->_obj = obj;
|
||||||
@ -212,19 +209,18 @@ bool TAssoc_array::add(const char* key, const TObject& obj, bool force)
|
|||||||
*/
|
*/
|
||||||
bool isnew = false;
|
bool isnew = false;
|
||||||
THash_object* o = _lookup(key, isnew, true);
|
THash_object* o = _lookup(key, isnew, true);
|
||||||
if (!isnew)
|
if (isnew)
|
||||||
{
|
{
|
||||||
if (force)
|
o->_obj = obj.dup();
|
||||||
{
|
return false;
|
||||||
if (o->_obj != NULL)
|
|
||||||
delete o->_obj;
|
|
||||||
o->_obj = obj.dup();
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
o->_obj = obj.dup();
|
if (force)
|
||||||
return false;
|
{
|
||||||
|
if (o->_obj != NULL)
|
||||||
|
delete o->_obj;
|
||||||
|
o->_obj = obj.dup();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// @doc EXTERNAL
|
// @doc EXTERNAL
|
||||||
|
@ -15,9 +15,7 @@
|
|||||||
//
|
//
|
||||||
// @base public | TObject
|
// @base public | TObject
|
||||||
class THash_object : public TObject
|
class THash_object : public TObject
|
||||||
|
// @author:(INTERNAL) Villa
|
||||||
// @author:(INTERNAL) Guido
|
|
||||||
|
|
||||||
{
|
{
|
||||||
// @cfriend TAssoc_array
|
// @cfriend TAssoc_array
|
||||||
friend class TAssoc_array;
|
friend class TAssoc_array;
|
||||||
@ -55,7 +53,7 @@ public:
|
|||||||
// @base public |TObject
|
// @base public |TObject
|
||||||
class TAssoc_array : public TContainer
|
class TAssoc_array : public TContainer
|
||||||
|
|
||||||
// @author:(INTERNAL) Guido
|
// @author:(INTERNAL) Villa
|
||||||
|
|
||||||
//@access:(INTERNAL) Private Member
|
//@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)
|
bool TRelation_application::save(bool check_dirty)
|
||||||
{
|
{
|
||||||
static bool was_dirty = FALSE;
|
static bool was_dirty = false;
|
||||||
|
|
||||||
int pos = _mask->id2pos(DLG_SAVEREC);
|
int pos = _mask->id2pos(DLG_SAVEREC);
|
||||||
if (pos < 0 || !_mask->fld(pos).active())
|
if (pos < 0 || !_mask->fld(pos).active())
|
||||||
@ -1018,7 +1055,7 @@ bool TRelation_application::save(bool check_dirty)
|
|||||||
const int mode = _mask->mode();
|
const int mode = _mask->mode();
|
||||||
if (check_dirty)
|
if (check_dirty)
|
||||||
{
|
{
|
||||||
const short dirty = _mask->dirty();
|
const short dirty = mask_field_dirty();
|
||||||
|
|
||||||
if (mode == MODE_QUERY)
|
if (mode == MODE_QUERY)
|
||||||
{
|
{
|
||||||
@ -1090,26 +1127,26 @@ bool TRelation_application::save(bool check_dirty)
|
|||||||
if (!ff.on_key(K_TAB))
|
if (!ff.on_key(K_TAB))
|
||||||
{
|
{
|
||||||
_mask->first_focus(-ff.dlg());
|
_mask->first_focus(-ff.dlg());
|
||||||
was_dirty = TRUE;
|
was_dirty = true;
|
||||||
return FALSE;
|
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
|
// check_fields sets the focus on the blocking field
|
||||||
_mask->first_focus(-_mask->focus_field().dlg());
|
_mask->first_focus(-_mask->focus_field().dlg());
|
||||||
was_dirty = TRUE;
|
was_dirty = true;
|
||||||
return FALSE;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
was_dirty = FALSE;
|
was_dirty = false;
|
||||||
|
|
||||||
TWait_cursor hourglass;
|
TWait_cursor hourglass;
|
||||||
if (mode == MODE_INS)
|
if (mode == MODE_INS)
|
||||||
{
|
{
|
||||||
bool changed = TRUE;
|
bool changed = true;
|
||||||
bool changed_key = FALSE;
|
bool changed_key = false;
|
||||||
|
|
||||||
while (changed)
|
while (changed)
|
||||||
{
|
{
|
||||||
|
@ -110,6 +110,7 @@ private:
|
|||||||
|
|
||||||
// @cmember:(INTERNAL) Sistema il bottone ricerca se necessario
|
// @cmember:(INTERNAL) Sistema il bottone ricerca se necessario
|
||||||
void set_find_button();
|
void set_find_button();
|
||||||
|
short mask_field_dirty() const;
|
||||||
|
|
||||||
// @access Protected Member
|
// @access Protected Member
|
||||||
protected: // TApplication
|
protected: // TApplication
|
||||||
|
@ -132,11 +132,11 @@ public: // TObject
|
|||||||
void replacef(TLocalisamfile* f, int lognum = 0,const char * relexprs="",int key=1);
|
void replacef(TLocalisamfile* f, int lognum = 0,const char * relexprs="",int key=1);
|
||||||
|
|
||||||
// @cmember Aggiunge il record corrente
|
// @cmember Aggiunge il record corrente
|
||||||
virtual int write (bool force = TRUE);
|
virtual int write (bool force = true);
|
||||||
// @cmember Riscrive il record corrente
|
// @cmember Riscrive il record corrente
|
||||||
virtual int rewrite (bool force = TRUE);
|
virtual int rewrite (bool force = true);
|
||||||
// @cmember Elimina il record corrente
|
// @cmember Elimina il record corrente
|
||||||
virtual int remove ();
|
virtual int remove();
|
||||||
|
|
||||||
// @cmember Controlla se e' stata raggiunta la fine del file
|
// @cmember Controlla se e' stata raggiunta la fine del file
|
||||||
// (se <p logicnum> = 0 dell'intera relazione, altrimenti del file indicato )
|
// (se <p logicnum> = 0 dell'intera relazione, altrimenti del file indicato )
|
||||||
|
Loading…
x
Reference in New Issue
Block a user