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 <mysys/hash.h>
27
const uint32_t NO_RECORD= UINT32_MAX;
31
const int HIGHFIND= 4;
32
const int HIGHUSED= 8;
31
34
typedef struct st_hash_info {
32
uint next; /* index to next key */
33
uchar *data; /* data for current entry */
35
/* index to next key */
37
/* 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,
41
static uint32_t hash_mask(uint32_t hashnr, uint32_t buffmax,
43
static void movelink(HASH_LINK *array, uint32_t pos,
44
uint32_t next_link, uint32_t newlink);
45
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)
48
static uint32_t calc_hash(const HASH *hash, const unsigned char *key,
43
51
uint32_t nr1=1, nr2=4;
44
hash->charset->coll->hash_sort(hash->charset,(const uchar*) key,length,&nr1,&nr2);
52
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)
57
_hash_init(HASH *hash,uint32_t growth_size, const CHARSET_INFO * const charset,
58
uint32_t size, size_t key_offset, size_t key_length,
60
void (*free_element)(void*),uint32_t flags CALLER_INFO_PROTO)
55
63
if (my_init_dynamic_array_ci(&hash->array, sizeof(HASH_LINK), size,
58
hash->free=0; /* Allow call to hash_free */
66
/* Allow call to hash_free */
61
70
hash->key_offset=key_offset;
146
155
return (char*) record+hash->key_offset;
149
/* Calculate pos according to keys */
158
/* Calculate pos according to keys */
151
static uint hash_mask(uint hashnr,uint buffmax,uint maxlength)
160
static uint32_t hash_mask(uint32_t hashnr,uint32_t buffmax,uint32_t maxlength)
153
162
if ((hashnr & (buffmax-1)) < maxlength) return (hashnr & (buffmax-1));
154
163
return (hashnr & ((buffmax >> 1) -1));
157
static uint hash_rec_mask(const HASH *hash, HASH_LINK *pos,
158
uint buffmax, uint maxlength)
166
static uint32_t hash_rec_mask(const HASH *hash, HASH_LINK *pos,
167
uint32_t buffmax, uint32_t maxlength)
161
uchar *key= (uchar*) hash_key(hash,pos->data,&length,0);
170
unsigned char *key= (unsigned char*) hash_key(hash,pos->data,&length,0);
162
171
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)
178
unsigned int rec_hashnr(HASH *hash,const unsigned char *record)
175
uchar *key= (uchar*) hash_key(hash,record,&length,0);
181
unsigned char *key= (unsigned char*) hash_key(hash,record,&length,0);
176
182
return calc_hash(hash,key,length);
180
uchar* hash_search(const HASH *hash, const uchar *key, size_t length)
186
unsigned char* hash_search(const HASH *hash, const unsigned char *key,
182
189
HASH_SEARCH_STATE state;
183
190
return hash_first(hash, key, length, &state);
187
194
Search after a record based on a key
190
Assigns the number of the found record to HASH_SEARCH_STATE state
197
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)
200
unsigned char* hash_first(const HASH *hash, const unsigned char *key,
202
HASH_SEARCH_STATE *current_record)
200
208
if (hash->records)
202
210
idx=hash_mask(calc_hash(hash,key,length ? length : hash->key_length),
203
hash->blength,hash->records);
211
hash->blength,hash->records);
206
214
pos= dynamic_element(&hash->array,idx,HASH_LINK*);
207
215
if (!hashcmp(hash,pos,key,length))
209
*current_record= idx;
217
*current_record= idx;
214
flag=0; /* Reset flag */
215
if (hash_rec_mask(hash,pos,hash->blength,hash->records) != idx)
216
break; /* Wrong link */
224
if (hash_rec_mask(hash,pos,hash->blength,hash->records) != idx)
219
229
while ((idx=pos->next) != NO_RECORD);
267
279
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
284
pos position of hash record to use in comparison
285
key key for comparison
277
If length is 0, comparison is done using the length of the
278
record being compared against.
289
If length is 0, comparison is done using the length of the
290
record being compared against.
281
= 0 key of record == key
282
!= 0 key of record != key
293
= 0 key of record == key
294
!= 0 key of record != key
285
static int hashcmp(const HASH *hash, HASH_LINK *pos, const uchar *key,
297
static int hashcmp(const HASH *hash, HASH_LINK *pos, const unsigned char *key,
288
300
size_t rec_keylength;
289
uchar *rec_key= (uchar*) hash_key(hash,pos->data,&rec_keylength,1);
301
unsigned char *rec_key= (unsigned char*) hash_key(hash, pos->data,
290
303
return ((length && length != rec_keylength) ||
291
my_strnncoll(hash->charset, (const uchar*) rec_key, rec_keylength,
292
(const uchar*) key, rec_keylength));
304
my_strnncoll(hash->charset, rec_key, rec_keylength,
305
key, rec_keylength));
296
/* Write a hash-key to the hash-index */
309
/* Write a hash-key to the hash-index */
298
bool my_hash_insert(HASH *info,const uchar *record)
311
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;
315
uint32_t halfbuff,hash_nr,first_index;
316
unsigned char *ptr_to_rec=NULL,*ptr_to_rec2=NULL;
304
317
HASH_LINK *data,*empty,*gpos=NULL,*gpos2=NULL,*pos;
306
319
if (HASH_UNIQUE & info->flags)
308
uchar *key= (uchar*) hash_key(info, record, &idx, 1);
321
unsigned char *key= (unsigned char*) hash_key(info, record, &idx, 1);
309
322
if (hash_search(info, key, idx))
310
return(true); /* Duplicate entry */
323
/* Duplicate entry */
314
328
if (!(empty=(HASH_LINK*) alloc_dynamic(&info->array)))
315
return(true); /* No more memory */
317
332
data=dynamic_element(&info->array,0,HASH_LINK*);
318
333
halfbuff= info->blength >> 1;
320
335
idx=first_index=info->records-halfbuff;
321
if (idx != info->records) /* If some records */
336
/* If some records */
337
if (idx != info->records)
326
342
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)
343
/* First loop; Check if ok */
345
if (hash_mask(hash_nr,info->blength,info->records) != first_index)
330
347
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;
349
/* Key will not move */
350
if (!(flag & LOWFIND))
354
flag=LOWFIND | HIGHFIND;
355
/* key shall be moved to the current empty position */
357
ptr_to_rec=pos->data;
358
/* This place is now free */
363
/* key isn't changed */
364
flag=LOWFIND | LOWUSED;
366
ptr_to_rec=pos->data;
371
if (!(flag & LOWUSED))
373
/* Change link of previous LOW-key */
374
gpos->data=ptr_to_rec;
375
gpos->next= (uint) (pos-data);
376
flag= (flag & HIGHFIND) | (LOWFIND | LOWUSED);
379
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;
384
/* key will be moved */
385
if (!(flag & HIGHFIND))
387
flag= (flag & LOWFIND) | HIGHFIND;
388
/* key shall be moved to the last (empty) position */
389
gpos2 = empty; empty=pos;
390
ptr_to_rec2=pos->data;
394
if (!(flag & HIGHUSED))
396
/* Change link of previous hash-key and save */
397
gpos2->data=ptr_to_rec2;
398
gpos2->next=(uint) (pos-data);
399
flag= (flag & LOWFIND) | (HIGHFIND | HIGHUSED);
402
ptr_to_rec2=pos->data;
385
406
while ((idx=pos->next) != NO_RECORD);
430
451
/******************************************************************************
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
******************************************************************************/
452
** Remove one record from hash-table. The record with the same record
454
** if there is a free-function it's called for record if found
455
*****************************************************************************/
436
bool hash_delete(HASH *hash,uchar *record)
457
bool hash_delete(HASH *hash,unsigned char *record)
438
uint blength,pos2,pos_hashnr,lastpos_hashnr,idx,empty_index;
459
uint32_t blength,pos2,pos_hashnr,lastpos_hashnr,idx,empty_index;
439
460
HASH_LINK *data,*lastpos,*gpos,*pos,*pos3,*empty;
440
461
if (!hash->records)
508
534
pos->next=empty_index;
511
VOID(pop_dynamic(&hash->array));
537
pop_dynamic(&hash->array);
513
(*hash->free)((uchar*) record);
539
(*hash->free)((unsigned char*) record);
518
Update keys when record has changed.
519
This is much more efficent than using a delete & insert.
544
Update keys when record has changed.
545
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)
548
bool hash_update(HASH *hash, unsigned char *record, unsigned char *old_key,
549
size_t old_key_length)
525
uint new_index,new_pos_index,blength,records,empty;
551
uint32_t new_index,new_pos_index,blength,records,empty;
527
553
HASH_LINK org_link,*data,*previous,*pos;
529
555
if (HASH_UNIQUE & hash->flags)
531
557
HASH_SEARCH_STATE state;
532
uchar *found, *new_key= (uchar*) hash_key(hash, record, &idx, 1);
558
unsigned char *found,
559
*new_key= (unsigned char*) hash_key(hash, record, &idx, 1);
533
561
if ((found= hash_first(hash, new_key, idx, &state)))
537
565
if (found != record)
538
566
return(1); /* Duplicate entry */
540
568
while ((found= hash_next(hash, new_key, idx, &state)));