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 */
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 */
21
17
/* One of key_length or key_length_offset must be given */
22
18
/* Key length of 0 isn't allowed */
25
#include "drizzled/my_hash.h"
26
#include "drizzled/charset.h"
27
#include "drizzled/charset_info.h"
32
const uint32_t NO_RECORD= UINT32_MAX;
36
const int HIGHFIND= 4;
37
const int HIGHUSED= 8;
20
#include "mysys_priv.h"
25
#define NO_RECORD ((uint) -1)
39
31
typedef struct st_hash_info {
40
/* index to next key */
42
/* data for current entry */
32
uint next; /* index to next key */
33
uchar *data; /* data for current entry */
46
static uint32_t hash_mask(uint32_t hashnr, uint32_t buffmax,
48
static void movelink(HASH_LINK *array, uint32_t pos,
49
uint32_t next_link, uint32_t newlink);
50
static int hashcmp(const HASH *hash, HASH_LINK *pos, const unsigned char *key,
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,
53
static uint32_t calc_hash(const HASH *hash, const unsigned char *key,
41
static uint calc_hash(const HASH *hash, const uchar *key, size_t length)
56
uint32_t nr1=1, nr2=4;
57
hash->charset->coll->hash_sort(hash->charset, key,length, &nr1, &nr2);
44
hash->charset->coll->hash_sort(hash->charset,(uchar*) key,length,&nr1,&nr2);
62
_hash_init(HASH *hash,uint32_t growth_size, const CHARSET_INFO * const charset,
63
uint32_t size, size_t key_offset, size_t key_length,
65
hash_free_key free_element, uint32_t flags)
49
_hash_init(HASH *hash,uint growth_size, CHARSET_INFO *charset,
50
ulong size, size_t key_offset, size_t key_length,
52
void (*free_element)(void*),uint flags CALLER_INFO_PROTO)
54
DBUG_ENTER("hash_init");
55
DBUG_PRINT("enter",("hash: 0x%lx size: %u", (long) hash, (uint) size));
68
58
if (my_init_dynamic_array_ci(&hash->array, sizeof(HASH_LINK), size,
71
/* Allow call to hash_free */
61
hash->free=0; /* Allow call to hash_free */
75
64
hash->key_offset=key_offset;
76
65
hash->key_length=key_length;
111
100
Free memory used by hash.
115
hash the hash to delete elements of
104
hash the hash to delete elements of
117
106
NOTES: Hash can't be reused without calling hash_init again.
120
109
void hash_free(HASH *hash)
111
DBUG_ENTER("hash_free");
112
DBUG_PRINT("enter",("hash: 0x%lx", (long) hash));
122
114
hash_free_elements(hash);
124
116
delete_dynamic(&hash->array);
122
Delete all elements from the hash (the hash itself is to be reused).
126
hash the hash to delete elements of
129
void my_hash_reset(HASH *hash)
131
DBUG_ENTER("my_hash_reset");
132
DBUG_PRINT("enter",("hash: 0x%lxd", (long) hash));
134
hash_free_elements(hash);
135
reset_dynamic(&hash->array);
136
/* Set row pointers so that the hash can be reused at once */
127
141
/* some helper functions */
130
This function is char* instead of unsigned char* as HPUX11 compiler can't
144
This function is char* instead of uchar* as HPUX11 compiler can't
131
145
handle inline functions that are not defined as native types
134
148
static inline char*
135
hash_key(const HASH *hash, const unsigned char *record, size_t *length,
149
hash_key(const HASH *hash, const uchar *record, size_t *length,
138
152
if (hash->get_key)
139
153
return (char*) (*hash->get_key)(record,length,first);
141
155
return (char*) record+hash->key_offset;
144
/* Calculate pos according to keys */
158
/* Calculate pos according to keys */
146
static uint32_t hash_mask(uint32_t hashnr,uint32_t buffmax,uint32_t maxlength)
160
static uint hash_mask(uint hashnr,uint buffmax,uint maxlength)
148
162
if ((hashnr & (buffmax-1)) < maxlength) return (hashnr & (buffmax-1));
149
163
return (hashnr & ((buffmax >> 1) -1));
152
static uint32_t hash_rec_mask(const HASH *hash, HASH_LINK *pos,
153
uint32_t buffmax, uint32_t maxlength)
166
static uint hash_rec_mask(const HASH *hash, HASH_LINK *pos,
167
uint buffmax, uint maxlength)
156
unsigned char *key= (unsigned char*) hash_key(hash,pos->data,&length,0);
170
uchar *key= (uchar*) hash_key(hash,pos->data,&length,0);
157
171
return hash_mask(calc_hash(hash,key,length),buffmax,maxlength);
176
/* for compilers which can not handle inline */
178
#if !defined(__USLC__) && !defined(__sgi)
164
unsigned int rec_hashnr(HASH *hash,const unsigned char *record)
181
unsigned int rec_hashnr(HASH *hash,const uchar *record)
167
unsigned char *key= (unsigned char*) hash_key(hash,record,&length,0);
184
uchar *key= (uchar*) hash_key(hash,record,&length,0);
168
185
return calc_hash(hash,key,length);
172
unsigned char* hash_search(const HASH *hash, const unsigned char *key,
189
uchar* hash_search(const HASH *hash, const uchar *key, size_t length)
175
191
HASH_SEARCH_STATE state;
176
192
return hash_first(hash, key, length, &state);
180
196
Search after a record based on a key
183
Assigns the number of the found record to HASH_SEARCH_STATE state
199
Assigns the number of the found record to HASH_SEARCH_STATE state
186
unsigned char* hash_first(const HASH *hash, const unsigned char *key,
188
HASH_SEARCH_STATE *current_record)
202
uchar* hash_first(const HASH *hash, const uchar *key, size_t length,
203
HASH_SEARCH_STATE *current_record)
207
DBUG_ENTER("hash_first");
194
210
if (hash->records)
196
212
idx=hash_mask(calc_hash(hash,key,length ? length : hash->key_length),
197
hash->blength,hash->records);
213
hash->blength,hash->records);
200
216
pos= dynamic_element(&hash->array,idx,HASH_LINK*);
201
217
if (!hashcmp(hash,pos,key,length))
203
*current_record= idx;
219
DBUG_PRINT("exit",("found key at %d",idx));
220
*current_record= idx;
221
DBUG_RETURN (pos->data);
210
if (hash_rec_mask(hash,pos,hash->blength,hash->records) != idx)
225
flag=0; /* Reset flag */
226
if (hash_rec_mask(hash,pos,hash->blength,hash->records) != idx)
227
break; /* Wrong link */
215
230
while ((idx=pos->next) != NO_RECORD);
217
232
*current_record= NO_RECORD;
221
/* Get next record with identical key */
222
/* Can only be called if previous calls was hash_search */
236
/* Get next record with identical key */
237
/* Can only be called if previous calls was hash_search */
224
unsigned char* hash_next(const HASH *hash, const unsigned char *key,
226
HASH_SEARCH_STATE *current_record)
239
uchar* hash_next(const HASH *hash, const uchar *key, size_t length,
240
HASH_SEARCH_STATE *current_record)
231
245
if (*current_record != NO_RECORD)
265
278
Compare a key in a record to a whole key. Return 0 if identical
270
pos position of hash record to use in comparison
271
key key for comparison
283
pos position of hash record to use in comparison
284
key key for comparison
275
If length is 0, comparison is done using the length of the
276
record being compared against.
288
If length is 0, comparison is done using the length of the
289
record being compared against.
279
= 0 key of record == key
280
!= 0 key of record != key
292
= 0 key of record == key
293
!= 0 key of record != key
283
static int hashcmp(const HASH *hash, HASH_LINK *pos, const unsigned char *key,
296
static int hashcmp(const HASH *hash, HASH_LINK *pos, const uchar *key,
286
299
size_t rec_keylength;
287
unsigned char *rec_key= (unsigned char*) hash_key(hash, pos->data,
300
uchar *rec_key= (uchar*) hash_key(hash,pos->data,&rec_keylength,1);
289
301
return ((length && length != rec_keylength) ||
290
my_strnncoll(hash->charset, rec_key, rec_keylength,
291
key, rec_keylength));
302
my_strnncoll(hash->charset, (uchar*) rec_key, rec_keylength,
303
(uchar*) key, rec_keylength));
295
/* Write a hash-key to the hash-index */
307
/* Write a hash-key to the hash-index */
297
bool my_hash_insert(HASH *info,const unsigned char *record)
309
my_bool my_hash_insert(HASH *info,const uchar *record)
301
uint32_t halfbuff,hash_nr,first_index;
302
unsigned char *ptr_to_rec=NULL,*ptr_to_rec2=NULL;
303
HASH_LINK *data,*empty,*gpos=NULL,*gpos2=NULL,*pos;
313
uint halfbuff,hash_nr,first_index;
314
uchar *ptr_to_rec,*ptr_to_rec2;
315
HASH_LINK *data,*empty,*gpos,*gpos2,*pos;
305
317
if (HASH_UNIQUE & info->flags)
307
unsigned char *key= (unsigned char*) hash_key(info, record, &idx, 1);
319
uchar *key= (uchar*) hash_key(info, record, &idx, 1);
308
320
if (hash_search(info, key, idx))
309
/* Duplicate entry */
321
return(TRUE); /* Duplicate entry */
314
325
if (!(empty=(HASH_LINK*) alloc_dynamic(&info->array)))
326
return(TRUE); /* No more memory */
318
328
data=dynamic_element(&info->array,0,HASH_LINK*);
319
329
halfbuff= info->blength >> 1;
321
idx= first_index= info->records-halfbuff;
322
/* If some records */
323
if (idx != info->records)
331
idx=first_index=info->records-halfbuff;
332
if (idx != info->records) /* If some records */
328
337
hash_nr=rec_hashnr(info,pos->data);
329
/* First loop; Check if ok */
331
if (hash_mask(hash_nr,info->blength,info->records) != first_index)
338
if (flag == 0) /* First loop; Check if ok */
339
if (hash_mask(hash_nr,info->blength,info->records) != first_index)
333
341
if (!(hash_nr & halfbuff))
335
/* Key will not move */
336
if (!(flag & LOWFIND))
340
flag=LOWFIND | HIGHFIND;
341
/* key shall be moved to the current empty position */
343
ptr_to_rec=pos->data;
344
/* This place is now free */
349
/* key isn't changed */
350
flag=LOWFIND | LOWUSED;
352
ptr_to_rec=pos->data;
357
if (!(flag & LOWUSED))
359
/* Change link of previous LOW-key */
360
gpos->data=ptr_to_rec;
361
gpos->next= (uint32_t) (pos-data);
362
flag= (flag & HIGHFIND) | (LOWFIND | LOWUSED);
365
ptr_to_rec=pos->data;
342
{ /* Key will not move */
343
if (!(flag & LOWFIND))
347
flag=LOWFIND | HIGHFIND;
348
/* key shall be moved to the current empty position */
350
ptr_to_rec=pos->data;
351
empty=pos; /* This place is now free */
355
flag=LOWFIND | LOWUSED; /* key isn't changed */
357
ptr_to_rec=pos->data;
362
if (!(flag & LOWUSED))
364
/* Change link of previous LOW-key */
365
gpos->data=ptr_to_rec;
366
gpos->next= (uint) (pos-data);
367
flag= (flag & HIGHFIND) | (LOWFIND | LOWUSED);
370
ptr_to_rec=pos->data;
370
/* key will be moved */
371
if (!(flag & HIGHFIND))
373
flag= (flag & LOWFIND) | HIGHFIND;
374
/* key shall be moved to the last (empty) position */
375
gpos2 = empty; empty=pos;
376
ptr_to_rec2=pos->data;
380
if (!(flag & HIGHUSED))
382
/* Change link of previous hash-key and save */
383
gpos2->data=ptr_to_rec2;
384
gpos2->next=(uint32_t) (pos-data);
385
flag= (flag & LOWFIND) | (HIGHFIND | HIGHUSED);
388
ptr_to_rec2=pos->data;
374
{ /* key will be moved */
375
if (!(flag & HIGHFIND))
377
flag= (flag & LOWFIND) | HIGHFIND;
378
/* key shall be moved to the last (empty) position */
379
gpos2 = empty; empty=pos;
380
ptr_to_rec2=pos->data;
384
if (!(flag & HIGHUSED))
386
/* Change link of previous hash-key and save */
387
gpos2->data=ptr_to_rec2;
388
gpos2->next=(uint) (pos-data);
389
flag= (flag & LOWFIND) | (HIGHFIND | HIGHUSED);
392
ptr_to_rec2=pos->data;
392
396
while ((idx=pos->next) != NO_RECORD);
437
441
/******************************************************************************
438
** Remove one record from hash-table. The record with the same record
440
** if there is a free-function it's called for record if found
441
*****************************************************************************/
442
** Remove one record from hash-table. The record with the same record
444
** if there is a free-function it's called for record if found
445
******************************************************************************/
443
bool hash_delete(HASH *hash,unsigned char *record)
447
my_bool hash_delete(HASH *hash,uchar *record)
445
uint32_t blength,pos2,pos_hashnr,lastpos_hashnr,idx,empty_index;
449
uint blength,pos2,pos_hashnr,lastpos_hashnr,idx,empty_index;
446
450
HASH_LINK *data,*lastpos,*gpos,*pos,*pos3,*empty;
451
DBUG_ENTER("hash_delete");
447
452
if (!hash->records)
450
455
blength=hash->blength;
451
456
data=dynamic_element(&hash->array,0,HASH_LINK*);
520
520
pos->next=empty_index;
523
pop_dynamic(&hash->array);
523
VOID(pop_dynamic(&hash->array));
525
(*hash->free)((unsigned char*) record);
529
unsigned char *hash_element(HASH *hash,uint32_t idx)
525
(*hash->free)((uchar*) record);
530
Update keys when record has changed.
531
This is much more efficent than using a delete & insert.
534
my_bool hash_update(HASH *hash, uchar *record, uchar *old_key,
535
size_t old_key_length)
537
uint new_index,new_pos_index,blength,records,empty;
539
HASH_LINK org_link,*data,*previous,*pos;
540
DBUG_ENTER("hash_update");
542
if (HASH_UNIQUE & hash->flags)
544
HASH_SEARCH_STATE state;
545
uchar *found, *new_key= (uchar*) hash_key(hash, record, &idx, 1);
546
if ((found= hash_first(hash, new_key, idx, &state)))
551
DBUG_RETURN(1); /* Duplicate entry */
553
while ((found= hash_next(hash, new_key, idx, &state)));
557
data=dynamic_element(&hash->array,0,HASH_LINK*);
558
blength=hash->blength; records=hash->records;
560
/* Search after record with key */
562
idx=hash_mask(calc_hash(hash, old_key,(old_key_length ?
566
new_index=hash_mask(rec_hashnr(hash,record),blength,records);
567
if (idx == new_index)
568
DBUG_RETURN(0); /* Nothing to do (No record check) */
573
if ((pos= data+idx)->data == record)
576
if ((idx=pos->next) == NO_RECORD)
577
DBUG_RETURN(1); /* Not found in links */
582
/* Relink record from current chain */
586
if (pos->next != NO_RECORD)
589
*pos= data[pos->next];
593
previous->next=pos->next; /* unlink pos */
595
/* Move data to correct position */
596
if (new_index == empty)
599
At this point record is unlinked from the old chain, thus it holds
600
random position. By the chance this position is equal to position
601
for the first element in the new chain. That means updated record
602
is the only record in the new chain.
607
Record was moved while unlinking it from the old chain.
608
Copy data to a new position.
610
data[empty]= org_link;
612
data[empty].next= NO_RECORD;
616
new_pos_index=hash_rec_mask(hash,pos,blength,records);
617
if (new_index != new_pos_index)
618
{ /* Other record in wrong position */
620
movelink(data,new_index,new_pos_index,empty);
621
org_link.next=NO_RECORD;
622
data[new_index]= org_link;
625
{ /* Link in chain at right position */
626
org_link.next=data[new_index].next;
627
data[empty]=org_link;
628
data[new_index].next=empty;
634
uchar *hash_element(HASH *hash,ulong idx)
531
636
if (idx < hash->records)
532
637
return dynamic_element(&hash->array,idx,HASH_LINK*)->data;
536
} /* namespace drizzled */
643
Replace old row with new row. This should only be used when key
647
void hash_replace(HASH *hash, HASH_SEARCH_STATE *current_record, uchar *new_row)
649
if (*current_record != NO_RECORD) /* Safety */
650
dynamic_element(&hash->array, *current_record, HASH_LINK*)->data= new_row;
656
my_bool hash_check(HASH *hash)
659
uint i,rec_link,found,max_links,seek,links,idx;
660
uint records,blength;
661
HASH_LINK *data,*hash_info;
663
records=hash->records; blength=hash->blength;
664
data=dynamic_element(&hash->array,0,HASH_LINK*);
667
for (i=found=max_links=seek=0 ; i < records ; i++)
669
if (hash_rec_mask(hash,data+i,blength,records) == i)
671
found++; seek++; links=1;
672
for (idx=data[i].next ;
673
idx != NO_RECORD && found < records + 1;
679
("Found pointer outside array to %d from link starting at %d",
685
if ((rec_link=hash_rec_mask(hash,hash_info,blength,records)) != i)
688
("Record in wrong link at %d: Start %d Record: 0x%lx Record-link %d",
689
idx, i, (long) hash_info->data, rec_link));
695
if (links > max_links) max_links=links;
698
if (found != records)
700
DBUG_PRINT("error",("Found %u of %u records", found, records));
705
("records: %u seeks: %d max links: %d hitrate: %.2f",
706
records,seek,max_links,(float) seek / (float) records));