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 */
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"
21
#include <mystrings/m_string.h>
22
#include <mystrings/m_ctype.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)
64
55
if (my_init_dynamic_array_ci(&hash->array, sizeof(HASH_LINK), size,
67
/* Allow call to hash_free */
58
hash->free=0; /* Allow call to hash_free */
71
61
hash->key_offset=key_offset;
156
146
return (char*) record+hash->key_offset;
159
/* Calculate pos according to keys */
149
/* Calculate pos according to keys */
161
static uint32_t hash_mask(uint32_t hashnr,uint32_t buffmax,uint32_t maxlength)
151
static uint hash_mask(uint hashnr,uint buffmax,uint maxlength)
163
153
if ((hashnr & (buffmax-1)) < maxlength) return (hashnr & (buffmax-1));
164
154
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)
157
static uint hash_rec_mask(const HASH *hash, HASH_LINK *pos,
158
uint buffmax, uint maxlength)
171
unsigned char *key= (unsigned char*) hash_key(hash,pos->data,&length,0);
161
uchar *key= (uchar*) hash_key(hash,pos->data,&length,0);
172
162
return hash_mask(calc_hash(hash,key,length),buffmax,maxlength);
167
/* for compilers which can not handle inline */
169
#if !defined(__USLC__) && !defined(__sgi)
179
unsigned int rec_hashnr(HASH *hash,const unsigned char *record)
172
unsigned int rec_hashnr(HASH *hash,const uchar *record)
182
unsigned char *key= (unsigned char*) hash_key(hash,record,&length,0);
175
uchar *key= (uchar*) hash_key(hash,record,&length,0);
183
176
return calc_hash(hash,key,length);
187
unsigned char* hash_search(const HASH *hash, const unsigned char *key,
180
uchar* hash_search(const HASH *hash, const uchar *key, size_t length)
190
182
HASH_SEARCH_STATE state;
191
183
return hash_first(hash, key, length, &state);
195
187
Search after a record based on a key
198
Assigns the number of the found record to HASH_SEARCH_STATE state
190
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)
193
uchar* hash_first(const HASH *hash, const uchar *key, size_t length,
194
HASH_SEARCH_STATE *current_record)
209
200
if (hash->records)
211
202
idx=hash_mask(calc_hash(hash,key,length ? length : hash->key_length),
212
hash->blength,hash->records);
203
hash->blength,hash->records);
215
206
pos= dynamic_element(&hash->array,idx,HASH_LINK*);
216
207
if (!hashcmp(hash,pos,key,length))
218
*current_record= idx;
209
*current_record= idx;
225
if (hash_rec_mask(hash,pos,hash->blength,hash->records) != idx)
214
flag=0; /* Reset flag */
215
if (hash_rec_mask(hash,pos,hash->blength,hash->records) != idx)
216
break; /* Wrong link */
230
219
while ((idx=pos->next) != NO_RECORD);
280
267
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
272
pos position of hash record to use in comparison
273
key key for comparison
290
If length is 0, comparison is done using the length of the
291
record being compared against.
277
If length is 0, comparison is done using the length of the
278
record being compared against.
294
= 0 key of record == key
295
!= 0 key of record != key
281
= 0 key of record == key
282
!= 0 key of record != key
298
static int hashcmp(const HASH *hash, HASH_LINK *pos, const unsigned char *key,
285
static int hashcmp(const HASH *hash, HASH_LINK *pos, const uchar *key,
301
288
size_t rec_keylength;
302
unsigned char *rec_key= (unsigned char*) hash_key(hash, pos->data,
289
uchar *rec_key= (uchar*) hash_key(hash,pos->data,&rec_keylength,1);
304
290
return ((length && length != rec_keylength) ||
305
my_strnncoll(hash->charset, rec_key, rec_keylength,
306
key, rec_keylength));
291
my_strnncoll(hash->charset, (uchar*) rec_key, rec_keylength,
292
(uchar*) key, rec_keylength));
310
/* Write a hash-key to the hash-index */
296
/* Write a hash-key to the hash-index */
312
bool my_hash_insert(HASH *info,const unsigned char *record)
298
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;
302
uint halfbuff,hash_nr,first_index;
303
uchar *ptr_to_rec=NULL,*ptr_to_rec2=NULL;
318
304
HASH_LINK *data,*empty,*gpos=NULL,*gpos2=NULL,*pos;
320
306
if (HASH_UNIQUE & info->flags)
322
unsigned char *key= (unsigned char*) hash_key(info, record, &idx, 1);
308
uchar *key= (uchar*) hash_key(info, record, &idx, 1);
323
309
if (hash_search(info, key, idx))
324
/* Duplicate entry */
310
return(true); /* Duplicate entry */
329
314
if (!(empty=(HASH_LINK*) alloc_dynamic(&info->array)))
315
return(true); /* No more memory */
333
317
data=dynamic_element(&info->array,0,HASH_LINK*);
334
318
halfbuff= info->blength >> 1;
336
320
idx=first_index=info->records-halfbuff;
337
/* If some records */
338
if (idx != info->records)
321
if (idx != info->records) /* If some records */
343
326
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)
327
if (flag == 0) /* First loop; Check if ok */
328
if (hash_mask(hash_nr,info->blength,info->records) != first_index)
348
330
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;
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;
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;
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;
407
385
while ((idx=pos->next) != NO_RECORD);
452
430
/******************************************************************************
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
*****************************************************************************/
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
******************************************************************************/
458
bool hash_delete(HASH *hash,unsigned char *record)
436
bool hash_delete(HASH *hash,uchar *record)
460
uint32_t blength,pos2,pos_hashnr,lastpos_hashnr,idx,empty_index;
438
uint blength,pos2,pos_hashnr,lastpos_hashnr,idx,empty_index;
461
439
HASH_LINK *data,*lastpos,*gpos,*pos,*pos3,*empty;
462
440
if (!hash->records)
535
508
pos->next=empty_index;
538
pop_dynamic(&hash->array);
511
VOID(pop_dynamic(&hash->array));
540
(*hash->free)((unsigned char*) record);
513
(*hash->free)((uchar*) record);
545
Update keys when record has changed.
546
This is much more efficent than using a delete & insert.
518
Update keys when record has changed.
519
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)
522
bool hash_update(HASH *hash, uchar *record, uchar *old_key,
523
size_t old_key_length)
552
uint32_t new_index,new_pos_index,blength,records,empty;
525
uint new_index,new_pos_index,blength,records,empty;
554
527
HASH_LINK org_link,*data,*previous,*pos;
556
529
if (HASH_UNIQUE & hash->flags)
558
531
HASH_SEARCH_STATE state;
559
unsigned char *found,
560
*new_key= (unsigned char*) hash_key(hash, record, &idx, 1);
532
uchar *found, *new_key= (uchar*) hash_key(hash, record, &idx, 1);
562
533
if ((found= hash_first(hash, new_key, idx, &state)))
566
537
if (found != record)
567
538
return(1); /* Duplicate entry */
569
540
while ((found= hash_next(hash, new_key, idx, &state)));