mattercontrol/original/Community.CsharpSqlite/src/hash_c.cs

368 lines
No EOL
9.3 KiB
C#

using System;
using System.Diagnostics;
using u32 = System.UInt32;
namespace Community.CsharpSqlite
{
public partial class Sqlite3
{
/*
** 2001 September 22
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
** This is the implementation of generic hash-tables
** used in SQLite.
*************************************************************************
** Included in SQLite3 port to C#-SQLite; 2008 Noah B Hart
** C#-SQLite is an independent reimplementation of the SQLite software library
**
** SQLITE_SOURCE_ID: 2010-08-23 18:52:01 42537b60566f288167f1b5864a5435986838e3a3
**
*************************************************************************
*/
//#include "sqliteInt.h"
//#include <assert.h>
/* Turn bulk memory into a hash table object by initializing the
** fields of the Hash structure.
**
** "pNew" is a pointer to the hash table that is to be initialized.
*/
private static void sqlite3HashInit(Hash pNew)
{
Debug.Assert(pNew != null);
pNew.first = null;
pNew.count = 0;
pNew.htsize = 0;
pNew.ht = null;
}
/* Remove all entries from a hash table. Reclaim all memory.
** Call this routine to delete a hash table or to reset a hash table
** to the empty state.
*/
private static void sqlite3HashClear(Hash pH)
{
HashElem elem; /* For looping over all elements of the table */
Debug.Assert(pH != null);
elem = pH.first;
pH.first = null;
//sqlite3_free( ref pH.ht );
pH.ht = null;
pH.htsize = 0;
while (elem != null)
{
HashElem next_elem = elem.next;
////sqlite3_free(ref elem );
elem = next_elem;
}
pH.count = 0;
}
/*
** The hashing function.
*/
private static u32 strHash(string z, int nKey)
{
int h = 0;
Debug.Assert(nKey >= 0);
int _z = 0;
while (nKey > 0)
{
h = (h << 3) ^ h ^ ((_z < z.Length) ? (int)sqlite3UpperToLower[(byte)z[_z++]] : 0);
nKey--;
}
return (u32)h;
}
/* Link pNew element into the hash table pH. If pEntry!=0 then also
** insert pNew into the pEntry hash bucket.
*/
private static void insertElement(
Hash pH, /* The complete hash table */
_ht pEntry, /* The entry into which pNew is inserted */
HashElem pNew /* The element to be inserted */
)
{
HashElem pHead; /* First element already in pEntry */
if (pEntry != null)
{
pHead = pEntry.count != 0 ? pEntry.chain : null;
pEntry.count++;
pEntry.chain = pNew;
}
else
{
pHead = null;
}
if (pHead != null)
{
pNew.next = pHead;
pNew.prev = pHead.prev;
if (pHead.prev != null)
{
pHead.prev.next = pNew;
}
else
{
pH.first = pNew;
}
pHead.prev = pNew;
}
else
{
pNew.next = pH.first;
if (pH.first != null)
{
pH.first.prev = pNew;
}
pNew.prev = null;
pH.first = pNew;
}
}
/* Resize the hash table so that it cantains "new_size" buckets.
**
** The hash table might fail to resize if sqlite3_malloc() fails or
** if the new size is the same as the prior size.
** Return TRUE if the resize occurs and false if not.
*/
private static bool rehash(ref Hash pH, u32 new_size)
{
_ht[] new_ht; /* The new hash table */
HashElem elem;
HashElem next_elem; /* For looping over existing elements */
#if SQLITE_MALLOC_SOFT_LIMIT
if( new_size*sizeof(struct _ht)>SQLITE_MALLOC_SOFT_LIMIT ){
new_size = SQLITE_MALLOC_SOFT_LIMIT/sizeof(struct _ht);
}
if( new_size==pH->htsize ) return false;
#endif
/* There is a call to sqlite3Malloc() inside rehash(). If there is
** already an allocation at pH.ht, then if this malloc() fails it
** is benign (since failing to resize a hash table is a performance
** hit only, not a fatal error).
*/
sqlite3BeginBenignMalloc();
new_ht = new _ht[new_size]; //(struct _ht )sqlite3Malloc( new_size*sizeof(struct _ht) );
for (int i = 0; i < new_size; i++)
new_ht[i] = new _ht();
sqlite3EndBenignMalloc();
if (new_ht == null)
return false;
//sqlite3_free( ref pH.ht );
pH.ht = new_ht;
// pH.htsize = new_size = sqlite3MallocSize(new_ht)/sizeof(struct _ht);
//memset(new_ht, 0, new_size*sizeof(struct _ht));
pH.htsize = new_size;
for (elem = pH.first, pH.first = null; elem != null; elem = next_elem)
{
u32 h = strHash(elem.pKey, elem.nKey) % new_size;
next_elem = elem.next;
insertElement(pH, new_ht[h], elem);
}
return true;
}
/* This function (for internal use only) locates an element in an
** hash table that matches the given key. The hash for this key has
** already been computed and is passed as the 4th parameter.
*/
private static HashElem findElementGivenHash(
Hash pH, /* The pH to be searched */
string pKey, /* The key we are searching for */
int nKey, /* Bytes in key (not counting zero terminator) */
u32 h /* The hash for this key. */
)
{
HashElem elem; /* Used to loop thru the element list */
int count; /* Number of elements left to test */
if (pH.ht != null && pH.ht[h] != null)
{
_ht pEntry = pH.ht[h];
elem = pEntry.chain;
count = (int)pEntry.count;
}
else
{
elem = pH.first;
count = (int)pH.count;
}
while (count-- > 0 && ALWAYS(elem))
{
if (elem.nKey == nKey && elem.pKey.Equals(pKey, StringComparison.OrdinalIgnoreCase))
{
return elem;
}
elem = elem.next;
}
return null;
}
/* Remove a single entry from the hash table given a pointer to that
** element and a hash on the element's key.
*/
private static void removeElementGivenHash(
Hash pH, /* The pH containing "elem" */
ref HashElem elem, /* The element to be removed from the pH */
u32 h /* Hash value for the element */
)
{
_ht pEntry;
if (elem.prev != null)
{
elem.prev.next = elem.next;
}
else
{
pH.first = elem.next;
}
if (elem.next != null)
{
elem.next.prev = elem.prev;
}
if (pH.ht != null && pH.ht[h] != null)
{
pEntry = pH.ht[h];
if (pEntry.chain == elem)
{
pEntry.chain = elem.next;
}
pEntry.count--;
Debug.Assert(pEntry.count >= 0);
}
//sqlite3_free( ref elem );
pH.count--;
if (pH.count <= 0)
{
Debug.Assert(pH.first == null);
Debug.Assert(pH.count == 0);
sqlite3HashClear(pH);
}
}
/* Attempt to locate an element of the hash table pH with a key
** that matches pKey,nKey. Return the data for this element if it is
** found, or NULL if there is no match.
*/
private static T sqlite3HashFind<T>(Hash pH, string pKey, int nKey, T nullType) where T : class
{
HashElem elem; /* The element that matches key */
u32 h; /* A hash on key */
Debug.Assert(pH != null);
Debug.Assert(pKey != null);
Debug.Assert(nKey >= 0);
if (pH.ht != null)
{
h = strHash(pKey, nKey) % pH.htsize;
}
else
{
h = 0;
}
elem = findElementGivenHash(pH, pKey, nKey, h);
return elem != null ? (T)elem.data : nullType;
}
/* Insert an element into the hash table pH. The key is pKey,nKey
** and the data is "data".
**
** If no element exists with a matching key, then a new
** element is created and NULL is returned.
**
** If another element already exists with the same key, then the
** new data replaces the old data and the old data is returned.
** The key is not copied in this instance. If a malloc fails, then
** the new data is returned and the hash table is unchanged.
**
** If the "data" parameter to this function is NULL, then the
** element corresponding to "key" is removed from the hash table.
*/
private static T sqlite3HashInsert<T>(ref Hash pH, string pKey, int nKey, T data) where T : class
{
u32 h; /* the hash of the key modulo hash table size */
HashElem elem; /* Used to loop thru the element list */
HashElem new_elem; /* New element added to the pH */
Debug.Assert(pH != null);
Debug.Assert(pKey != null);
Debug.Assert(nKey >= 0);
if (pH.htsize != 0)
{
h = strHash(pKey, nKey) % pH.htsize;
}
else
{
h = 0;
}
elem = findElementGivenHash(pH, pKey, nKey, h);
if (elem != null)
{
T old_data = (T)elem.data;
if (data == null)
{
removeElementGivenHash(pH, ref elem, h);
}
else
{
elem.data = data;
elem.pKey = pKey;
Debug.Assert(nKey == elem.nKey);
}
return old_data;
}
if (data == null)
return data;
new_elem = new HashElem();//(HashElem)sqlite3Malloc( sizeof(HashElem) );
if (new_elem == null)
return data;
new_elem.pKey = pKey;
new_elem.nKey = nKey;
new_elem.data = data;
pH.count++;
if (pH.count >= 10 && pH.count > 2 * pH.htsize)
{
if (rehash(ref pH, pH.count * 2))
{
Debug.Assert(pH.htsize > 0);
h = strHash(pKey, nKey) % pH.htsize;
}
}
if (pH.ht != null)
{
insertElement(pH, pH.ht[h], new_elem);
}
else
{
insertElement(pH, null, new_elem);
}
return null;
}
}
}