1
/* Copyright (C) 2000 MySQL AB
3
This program is free software; you can redistribute it and/or modify
4
it under the terms of the GNU General Public License as published by
5
the Free Software Foundation; version 2 of the License.
7
This program is distributed in the hope that it will be useful,
8
but WITHOUT ANY WARRANTY; without even the implied warranty of
9
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
GNU General Public License for more details.
12
You should have received a copy of the GNU General Public License
13
along with this program; if not, write to the Free Software
14
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
16
/* The hash functions used for saveing keys */
1
/* -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
2
* vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
4
* Copyright (C) 2008 Sun Microsystems
6
* This program is free software; you can redistribute it and/or modify
7
* it under the terms of the GNU General Public License as published by
8
* the Free Software Foundation; version 2 of the License.
10
* This program is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
15
* You should have received a copy of the GNU General Public License
16
* along with this program; if not, write to the Free Software
17
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20
/* The hash functions used for saving keys */
17
21
/* One of key_length or key_length_offset must be given */
18
22
/* Key length of 0 isn't allowed */
20
#include "mysys_priv.h"
21
#include <mystrings/m_string.h>
22
#include <mystrings/m_ctype.h>
25
#define NO_RECORD ((uint) -1)
25
#include "drizzled/my_hash.h"
26
#include "drizzled/charset.h"
27
#include "drizzled/charset_info.h"
29
const uint32_t NO_RECORD= UINT32_MAX;
33
const int HIGHFIND= 4;
34
const int HIGHUSED= 8;
31
36
typedef struct st_hash_info {
32
uint next; /* index to next key */
33
uchar *data; /* data for current entry */
37
/* index to next key */
39
/* data for current entry */
36
static uint hash_mask(uint hashnr,uint buffmax,uint maxlength);
37
static void movelink(HASH_LINK *array,uint pos,uint next_link,uint newlink);
38
static int hashcmp(const HASH *hash, HASH_LINK *pos, const uchar *key,
43
static uint32_t hash_mask(uint32_t hashnr, uint32_t buffmax,
45
static void movelink(HASH_LINK *array, uint32_t pos,
46
uint32_t next_link, uint32_t newlink);
47
static int hashcmp(const HASH *hash, HASH_LINK *pos, const unsigned char *key,
41
static uint calc_hash(const HASH *hash, const uchar *key, size_t length)
50
static uint32_t calc_hash(const HASH *hash, const unsigned char *key,
43
53
uint32_t nr1=1, nr2=4;
44
hash->charset->coll->hash_sort(hash->charset,(const uchar*) key,length,&nr1,&nr2);
54
hash->charset->coll->hash_sort(hash->charset, key,length, &nr1, &nr2);
49
_hash_init(HASH *hash,uint growth_size, const CHARSET_INFO * const charset,
50
uint32_t size, size_t key_offset, size_t key_length,
52
void (*free_element)(void*),uint flags CALLER_INFO_PROTO)
59
_hash_init(HASH *hash,uint32_t growth_size, const CHARSET_INFO * const charset,
60
uint32_t size, size_t key_offset, size_t key_length,
62
hash_free_key free_element, uint32_t flags)
55
65
if (my_init_dynamic_array_ci(&hash->array, sizeof(HASH_LINK), size,
58
hash->free=0; /* Allow call to hash_free */
68
/* Allow call to hash_free */
61
72
hash->key_offset=key_offset;
116
Delete all elements from the hash (the hash itself is to be reused).
120
hash the hash to delete elements of
123
void my_hash_reset(HASH *hash)
125
hash_free_elements(hash);
126
reset_dynamic(&hash->array);
127
/* Set row pointers so that the hash can be reused at once */
132
125
/* some helper functions */
135
This function is char* instead of uchar* as HPUX11 compiler can't
128
This function is char* instead of unsigned char* as HPUX11 compiler can't
136
129
handle inline functions that are not defined as native types
139
132
static inline char*
140
hash_key(const HASH *hash, const uchar *record, size_t *length,
133
hash_key(const HASH *hash, const unsigned char *record, size_t *length,
143
136
if (hash->get_key)
146
139
return (char*) record+hash->key_offset;
149
/* Calculate pos according to keys */
142
/* Calculate pos according to keys */
151
static uint hash_mask(uint hashnr,uint buffmax,uint maxlength)
144
static uint32_t hash_mask(uint32_t hashnr,uint32_t buffmax,uint32_t maxlength)
153
146
if ((hashnr & (buffmax-1)) < maxlength) return (hashnr & (buffmax-1));
154
147
return (hashnr & ((buffmax >> 1) -1));
157
static uint hash_rec_mask(const HASH *hash, HASH_LINK *pos,
158
uint buffmax, uint maxlength)
150
static uint32_t hash_rec_mask(const HASH *hash, HASH_LINK *pos,
151
uint32_t buffmax, uint32_t maxlength)
161
uchar *key= (uchar*) hash_key(hash,pos->data,&length,0);
154
unsigned char *key= (unsigned char*) hash_key(hash,pos->data,&length,0);
162
155
return hash_mask(calc_hash(hash,key,length),buffmax,maxlength);
167
/* for compilers which can not handle inline */
169
#if !defined(__USLC__) && !defined(__sgi)
172
unsigned int rec_hashnr(HASH *hash,const uchar *record)
162
unsigned int rec_hashnr(HASH *hash,const unsigned char *record)
175
uchar *key= (uchar*) hash_key(hash,record,&length,0);
165
unsigned char *key= (unsigned char*) hash_key(hash,record,&length,0);
176
166
return calc_hash(hash,key,length);
180
uchar* hash_search(const HASH *hash, const uchar *key, size_t length)
170
unsigned char* hash_search(const HASH *hash, const unsigned char *key,
182
173
HASH_SEARCH_STATE state;
183
174
return hash_first(hash, key, length, &state);
187
178
Search after a record based on a key
190
Assigns the number of the found record to HASH_SEARCH_STATE state
181
Assigns the number of the found record to HASH_SEARCH_STATE state
193
uchar* hash_first(const HASH *hash, const uchar *key, size_t length,
194
HASH_SEARCH_STATE *current_record)
184
unsigned char* hash_first(const HASH *hash, const unsigned char *key,
186
HASH_SEARCH_STATE *current_record)
200
192
if (hash->records)
202
194
idx=hash_mask(calc_hash(hash,key,length ? length : hash->key_length),
203
hash->blength,hash->records);
195
hash->blength,hash->records);
206
198
pos= dynamic_element(&hash->array,idx,HASH_LINK*);
207
199
if (!hashcmp(hash,pos,key,length))
209
*current_record= idx;
201
*current_record= idx;
214
flag=0; /* Reset flag */
215
if (hash_rec_mask(hash,pos,hash->blength,hash->records) != idx)
216
break; /* Wrong link */
208
if (hash_rec_mask(hash,pos,hash->blength,hash->records) != idx)
219
213
while ((idx=pos->next) != NO_RECORD);
267
263
Compare a key in a record to a whole key. Return 0 if identical
272
pos position of hash record to use in comparison
273
key key for comparison
268
pos position of hash record to use in comparison
269
key key for comparison
277
If length is 0, comparison is done using the length of the
278
record being compared against.
273
If length is 0, comparison is done using the length of the
274
record being compared against.
281
= 0 key of record == key
282
!= 0 key of record != key
277
= 0 key of record == key
278
!= 0 key of record != key
285
static int hashcmp(const HASH *hash, HASH_LINK *pos, const uchar *key,
281
static int hashcmp(const HASH *hash, HASH_LINK *pos, const unsigned char *key,
288
284
size_t rec_keylength;
289
uchar *rec_key= (uchar*) hash_key(hash,pos->data,&rec_keylength,1);
285
unsigned char *rec_key= (unsigned char*) hash_key(hash, pos->data,
290
287
return ((length && length != rec_keylength) ||
291
my_strnncoll(hash->charset, (const uchar*) rec_key, rec_keylength,
292
(const uchar*) key, rec_keylength));
288
my_strnncoll(hash->charset, rec_key, rec_keylength,
289
key, rec_keylength));
296
/* Write a hash-key to the hash-index */
293
/* Write a hash-key to the hash-index */
298
bool my_hash_insert(HASH *info,const uchar *record)
295
bool my_hash_insert(HASH *info,const unsigned char *record)
302
uint halfbuff,hash_nr,first_index;
303
uchar *ptr_to_rec=NULL,*ptr_to_rec2=NULL;
299
uint32_t halfbuff,hash_nr,first_index;
300
unsigned char *ptr_to_rec=NULL,*ptr_to_rec2=NULL;
304
301
HASH_LINK *data,*empty,*gpos=NULL,*gpos2=NULL,*pos;
306
303
if (HASH_UNIQUE & info->flags)
308
uchar *key= (uchar*) hash_key(info, record, &idx, 1);
305
unsigned char *key= (unsigned char*) hash_key(info, record, &idx, 1);
309
306
if (hash_search(info, key, idx))
310
return(true); /* Duplicate entry */
307
/* Duplicate entry */
314
312
if (!(empty=(HASH_LINK*) alloc_dynamic(&info->array)))
315
return(true); /* No more memory */
317
316
data=dynamic_element(&info->array,0,HASH_LINK*);
318
317
halfbuff= info->blength >> 1;
320
idx=first_index=info->records-halfbuff;
321
if (idx != info->records) /* If some records */
319
idx= first_index= info->records-halfbuff;
320
/* If some records */
321
if (idx != info->records)
326
326
hash_nr=rec_hashnr(info,pos->data);
327
if (flag == 0) /* First loop; Check if ok */
328
if (hash_mask(hash_nr,info->blength,info->records) != first_index)
327
/* First loop; Check if ok */
329
if (hash_mask(hash_nr,info->blength,info->records) != first_index)
330
331
if (!(hash_nr & halfbuff))
331
{ /* Key will not move */
332
if (!(flag & LOWFIND))
336
flag=LOWFIND | HIGHFIND;
337
/* key shall be moved to the current empty position */
339
ptr_to_rec=pos->data;
340
empty=pos; /* This place is now free */
344
flag=LOWFIND | LOWUSED; /* key isn't changed */
346
ptr_to_rec=pos->data;
351
if (!(flag & LOWUSED))
353
/* Change link of previous LOW-key */
354
gpos->data=ptr_to_rec;
355
gpos->next= (uint) (pos-data);
356
flag= (flag & HIGHFIND) | (LOWFIND | LOWUSED);
359
ptr_to_rec=pos->data;
333
/* Key will not move */
334
if (!(flag & LOWFIND))
338
flag=LOWFIND | HIGHFIND;
339
/* key shall be moved to the current empty position */
341
ptr_to_rec=pos->data;
342
/* This place is now free */
347
/* key isn't changed */
348
flag=LOWFIND | LOWUSED;
350
ptr_to_rec=pos->data;
355
if (!(flag & LOWUSED))
357
/* Change link of previous LOW-key */
358
gpos->data=ptr_to_rec;
359
gpos->next= (uint32_t) (pos-data);
360
flag= (flag & HIGHFIND) | (LOWFIND | LOWUSED);
363
ptr_to_rec=pos->data;
363
{ /* key will be moved */
364
if (!(flag & HIGHFIND))
366
flag= (flag & LOWFIND) | HIGHFIND;
367
/* key shall be moved to the last (empty) position */
368
gpos2 = empty; empty=pos;
369
ptr_to_rec2=pos->data;
373
if (!(flag & HIGHUSED))
375
/* Change link of previous hash-key and save */
376
gpos2->data=ptr_to_rec2;
377
gpos2->next=(uint) (pos-data);
378
flag= (flag & LOWFIND) | (HIGHFIND | HIGHUSED);
381
ptr_to_rec2=pos->data;
368
/* key will be moved */
369
if (!(flag & HIGHFIND))
371
flag= (flag & LOWFIND) | HIGHFIND;
372
/* key shall be moved to the last (empty) position */
373
gpos2 = empty; empty=pos;
374
ptr_to_rec2=pos->data;
378
if (!(flag & HIGHUSED))
380
/* Change link of previous hash-key and save */
381
gpos2->data=ptr_to_rec2;
382
gpos2->next=(uint32_t) (pos-data);
383
flag= (flag & LOWFIND) | (HIGHFIND | HIGHUSED);
386
ptr_to_rec2=pos->data;
385
390
while ((idx=pos->next) != NO_RECORD);
430
435
/******************************************************************************
431
** Remove one record from hash-table. The record with the same record
433
** if there is a free-function it's called for record if found
434
******************************************************************************/
436
** Remove one record from hash-table. The record with the same record
438
** if there is a free-function it's called for record if found
439
*****************************************************************************/
436
bool hash_delete(HASH *hash,uchar *record)
441
bool hash_delete(HASH *hash,unsigned char *record)
438
uint blength,pos2,pos_hashnr,lastpos_hashnr,idx,empty_index;
443
uint32_t blength,pos2,pos_hashnr,lastpos_hashnr,idx,empty_index;
439
444
HASH_LINK *data,*lastpos,*gpos,*pos,*pos3,*empty;
440
445
if (!hash->records)
508
518
pos->next=empty_index;
511
VOID(pop_dynamic(&hash->array));
521
pop_dynamic(&hash->array);
513
(*hash->free)((uchar*) record);
518
Update keys when record has changed.
519
This is much more efficent than using a delete & insert.
522
bool hash_update(HASH *hash, uchar *record, uchar *old_key,
523
size_t old_key_length)
525
uint new_index,new_pos_index,blength,records,empty;
527
HASH_LINK org_link,*data,*previous,*pos;
529
if (HASH_UNIQUE & hash->flags)
531
HASH_SEARCH_STATE state;
532
uchar *found, *new_key= (uchar*) hash_key(hash, record, &idx, 1);
533
if ((found= hash_first(hash, new_key, idx, &state)))
538
return(1); /* Duplicate entry */
540
while ((found= hash_next(hash, new_key, idx, &state)));
544
data=dynamic_element(&hash->array,0,HASH_LINK*);
545
blength=hash->blength; records=hash->records;
547
/* Search after record with key */
549
idx=hash_mask(calc_hash(hash, old_key,(old_key_length ?
553
new_index=hash_mask(rec_hashnr(hash,record),blength,records);
554
if (idx == new_index)
555
return(0); /* Nothing to do (No record check) */
560
if ((pos= data+idx)->data == record)
563
if ((idx=pos->next) == NO_RECORD)
564
return(1); /* Not found in links */
569
/* Relink record from current chain */
573
if (pos->next != NO_RECORD)
576
*pos= data[pos->next];
580
previous->next=pos->next; /* unlink pos */
582
/* Move data to correct position */
583
if (new_index == empty)
586
At this point record is unlinked from the old chain, thus it holds
587
random position. By the chance this position is equal to position
588
for the first element in the new chain. That means updated record
589
is the only record in the new chain.
594
Record was moved while unlinking it from the old chain.
595
Copy data to a new position.
597
data[empty]= org_link;
599
data[empty].next= NO_RECORD;
603
new_pos_index=hash_rec_mask(hash,pos,blength,records);
604
if (new_index != new_pos_index)
605
{ /* Other record in wrong position */
607
movelink(data,new_index,new_pos_index,empty);
608
org_link.next=NO_RECORD;
609
data[new_index]= org_link;
612
{ /* Link in chain at right position */
613
org_link.next=data[new_index].next;
614
data[empty]=org_link;
615
data[new_index].next=empty;
621
uchar *hash_element(HASH *hash,uint32_t idx)
523
(*hash->free)((unsigned char*) record);
527
unsigned char *hash_element(HASH *hash,uint32_t idx)
623
529
if (idx < hash->records)
624
530
return dynamic_element(&hash->array,idx,HASH_LINK*)->data;
630
Replace old row with new row. This should only be used when key
634
void hash_replace(HASH *hash, HASH_SEARCH_STATE *current_record, uchar *new_row)
636
if (*current_record != NO_RECORD) /* Safety */
637
dynamic_element(&hash->array, *current_record, HASH_LINK*)->data= new_row;