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 */
24
#include <drizzled/global.h>
26
#include <mysys/hash.h>
28
const uint32_t NO_RECORD= UINT32_MAX;
32
const int HIGHFIND= 4;
33
const int HIGHUSED= 8;
20
#include "mysys_priv.h"
25
#define NO_RECORD ((uint) -1)
35
31
typedef struct st_hash_info {
36
/* index to next key */
38
/* data for current entry */
32
uint next; /* index to next key */
33
uchar *data; /* data for current entry */
42
static uint32_t hash_mask(uint32_t hashnr, uint32_t buffmax,
44
static void movelink(HASH_LINK *array, uint32_t pos,
45
uint32_t next_link, uint32_t newlink);
46
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,
49
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)
52
uint32_t nr1=1, nr2=4;
53
hash->charset->coll->hash_sort(hash->charset, key,length, &nr1, &nr2);
44
hash->charset->coll->hash_sort(hash->charset,(uchar*) key,length,&nr1,&nr2);
58
_hash_init(HASH *hash,uint32_t growth_size, const CHARSET_INFO * const charset,
59
uint32_t size, size_t key_offset, size_t key_length,
61
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));
64
58
if (my_init_dynamic_array_ci(&hash->array, sizeof(HASH_LINK), size,
67
/* Allow call to hash_free */
61
hash->free=0; /* Allow call to hash_free */
71
64
hash->key_offset=key_offset;
72
65
hash->key_length=key_length;
126
122
Delete all elements from the hash (the hash itself is to be reused).
130
hash the hash to delete elements of
126
hash the hash to delete elements of
133
129
void my_hash_reset(HASH *hash)
131
DBUG_ENTER("my_hash_reset");
132
DBUG_PRINT("enter",("hash: 0x%lxd", (long) hash));
135
134
hash_free_elements(hash);
136
135
reset_dynamic(&hash->array);
137
136
/* Set row pointers so that the hash can be reused at once */
138
137
hash->blength= 1;
142
141
/* some helper functions */
145
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
146
145
handle inline functions that are not defined as native types
149
148
static inline char*
150
hash_key(const HASH *hash, const unsigned char *record, size_t *length,
149
hash_key(const HASH *hash, const uchar *record, size_t *length,
153
152
if (hash->get_key)
154
153
return (char*) (*hash->get_key)(record,length,first);
156
155
return (char*) record+hash->key_offset;
159
/* Calculate pos according to keys */
158
/* Calculate pos according to keys */
161
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)
163
162
if ((hashnr & (buffmax-1)) < maxlength) return (hashnr & (buffmax-1));
164
163
return (hashnr & ((buffmax >> 1) -1));
167
static uint32_t hash_rec_mask(const HASH *hash, HASH_LINK *pos,
168
uint32_t buffmax, uint32_t maxlength)
166
static uint hash_rec_mask(const HASH *hash, HASH_LINK *pos,
167
uint buffmax, uint maxlength)
171
unsigned char *key= (unsigned char*) hash_key(hash,pos->data,&length,0);
170
uchar *key= (uchar*) hash_key(hash,pos->data,&length,0);
172
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)
179
unsigned int rec_hashnr(HASH *hash,const unsigned char *record)
181
unsigned int rec_hashnr(HASH *hash,const uchar *record)
182
unsigned char *key= (unsigned char*) hash_key(hash,record,&length,0);
184
uchar *key= (uchar*) hash_key(hash,record,&length,0);
183
185
return calc_hash(hash,key,length);
187
unsigned char* hash_search(const HASH *hash, const unsigned char *key,
189
uchar* hash_search(const HASH *hash, const uchar *key, size_t length)
190
191
HASH_SEARCH_STATE state;
191
192
return hash_first(hash, key, length, &state);
195
196
Search after a record based on a key
198
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
201
unsigned char* hash_first(const HASH *hash, const unsigned char *key,
203
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");
209
210
if (hash->records)
211
212
idx=hash_mask(calc_hash(hash,key,length ? length : hash->key_length),
212
hash->blength,hash->records);
213
hash->blength,hash->records);
215
216
pos= dynamic_element(&hash->array,idx,HASH_LINK*);
216
217
if (!hashcmp(hash,pos,key,length))
218
*current_record= idx;
219
DBUG_PRINT("exit",("found key at %d",idx));
220
*current_record= idx;
221
DBUG_RETURN (pos->data);
225
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 */
230
230
while ((idx=pos->next) != NO_RECORD);
232
232
*current_record= NO_RECORD;
236
/* Get next record with identical key */
237
/* 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 */
239
unsigned char* hash_next(const HASH *hash, const unsigned char *key,
241
HASH_SEARCH_STATE *current_record)
239
uchar* hash_next(const HASH *hash, const uchar *key, size_t length,
240
HASH_SEARCH_STATE *current_record)
246
245
if (*current_record != NO_RECORD)
280
278
Compare a key in a record to a whole key. Return 0 if identical
285
pos position of hash record to use in comparison
286
key key for comparison
283
pos position of hash record to use in comparison
284
key key for comparison
290
If length is 0, comparison is done using the length of the
291
record being compared against.
288
If length is 0, comparison is done using the length of the
289
record being compared against.
294
= 0 key of record == key
295
!= 0 key of record != key
292
= 0 key of record == key
293
!= 0 key of record != key
298
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,
301
299
size_t rec_keylength;
302
unsigned char *rec_key= (unsigned char*) hash_key(hash, pos->data,
300
uchar *rec_key= (uchar*) hash_key(hash,pos->data,&rec_keylength,1);
304
301
return ((length && length != rec_keylength) ||
305
my_strnncoll(hash->charset, rec_key, rec_keylength,
306
key, rec_keylength));
302
my_strnncoll(hash->charset, (uchar*) rec_key, rec_keylength,
303
(uchar*) key, rec_keylength));
310
/* Write a hash-key to the hash-index */
307
/* Write a hash-key to the hash-index */
312
bool my_hash_insert(HASH *info,const unsigned char *record)
309
my_bool my_hash_insert(HASH *info,const uchar *record)
316
uint32_t halfbuff,hash_nr,first_index;
317
unsigned char *ptr_to_rec=NULL,*ptr_to_rec2=NULL;
318
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;
320
317
if (HASH_UNIQUE & info->flags)
322
unsigned char *key= (unsigned char*) hash_key(info, record, &idx, 1);
319
uchar *key= (uchar*) hash_key(info, record, &idx, 1);
323
320
if (hash_search(info, key, idx))
324
/* Duplicate entry */
321
return(TRUE); /* Duplicate entry */
329
325
if (!(empty=(HASH_LINK*) alloc_dynamic(&info->array)))
326
return(TRUE); /* No more memory */
333
328
data=dynamic_element(&info->array,0,HASH_LINK*);
334
329
halfbuff= info->blength >> 1;
336
331
idx=first_index=info->records-halfbuff;
337
/* If some records */
338
if (idx != info->records)
332
if (idx != info->records) /* If some records */
343
337
hash_nr=rec_hashnr(info,pos->data);
344
/* First loop; Check if ok */
346
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)
348
341
if (!(hash_nr & halfbuff))
350
/* Key will not move */
351
if (!(flag & LOWFIND))
355
flag=LOWFIND | HIGHFIND;
356
/* key shall be moved to the current empty position */
358
ptr_to_rec=pos->data;
359
/* This place is now free */
364
/* key isn't changed */
365
flag=LOWFIND | LOWUSED;
367
ptr_to_rec=pos->data;
372
if (!(flag & LOWUSED))
374
/* Change link of previous LOW-key */
375
gpos->data=ptr_to_rec;
376
gpos->next= (uint) (pos-data);
377
flag= (flag & HIGHFIND) | (LOWFIND | LOWUSED);
380
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;
385
/* key will be moved */
386
if (!(flag & HIGHFIND))
388
flag= (flag & LOWFIND) | HIGHFIND;
389
/* key shall be moved to the last (empty) position */
390
gpos2 = empty; empty=pos;
391
ptr_to_rec2=pos->data;
395
if (!(flag & HIGHUSED))
397
/* Change link of previous hash-key and save */
398
gpos2->data=ptr_to_rec2;
399
gpos2->next=(uint) (pos-data);
400
flag= (flag & LOWFIND) | (HIGHFIND | HIGHUSED);
403
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;
407
396
while ((idx=pos->next) != NO_RECORD);
452
441
/******************************************************************************
453
** Remove one record from hash-table. The record with the same record
455
** if there is a free-function it's called for record if found
456
*****************************************************************************/
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
******************************************************************************/
458
bool hash_delete(HASH *hash,unsigned char *record)
447
my_bool hash_delete(HASH *hash,uchar *record)
460
uint32_t blength,pos2,pos_hashnr,lastpos_hashnr,idx,empty_index;
449
uint blength,pos2,pos_hashnr,lastpos_hashnr,idx,empty_index;
461
450
HASH_LINK *data,*lastpos,*gpos,*pos,*pos3,*empty;
451
DBUG_ENTER("hash_delete");
462
452
if (!hash->records)
465
455
blength=hash->blength;
466
456
data=dynamic_element(&hash->array,0,HASH_LINK*);
535
520
pos->next=empty_index;
538
pop_dynamic(&hash->array);
523
VOID(pop_dynamic(&hash->array));
540
(*hash->free)((unsigned char*) record);
525
(*hash->free)((uchar*) record);
545
Update keys when record has changed.
546
This is much more efficent than using a delete & insert.
530
Update keys when record has changed.
531
This is much more efficent than using a delete & insert.
549
bool hash_update(HASH *hash, unsigned char *record, unsigned char *old_key,
550
size_t old_key_length)
534
my_bool hash_update(HASH *hash, uchar *record, uchar *old_key,
535
size_t old_key_length)
552
uint32_t new_index,new_pos_index,blength,records,empty;
537
uint new_index,new_pos_index,blength,records,empty;
554
539
HASH_LINK org_link,*data,*previous,*pos;
540
DBUG_ENTER("hash_update");
556
542
if (HASH_UNIQUE & hash->flags)
558
544
HASH_SEARCH_STATE state;
559
unsigned char *found,
560
*new_key= (unsigned char*) hash_key(hash, record, &idx, 1);
545
uchar *found, *new_key= (uchar*) hash_key(hash, record, &idx, 1);
562
546
if ((found= hash_first(hash, new_key, idx, &state)))
566
550
if (found != record)
567
return(1); /* Duplicate entry */
551
DBUG_RETURN(1); /* Duplicate entry */
569
553
while ((found= hash_next(hash, new_key, idx, &state)));
663
void hash_replace(HASH *hash, HASH_SEARCH_STATE *current_record,
664
unsigned char *new_row)
647
void hash_replace(HASH *hash, HASH_SEARCH_STATE *current_record, uchar *new_row)
666
649
if (*current_record != NO_RECORD) /* Safety */
667
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));