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"
21
#include <mystrings/m_string.h>
22
#include <mystrings/m_ctype.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
43
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,(const 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, 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)
68
55
if (my_init_dynamic_array_ci(&hash->array, sizeof(HASH_LINK), size,
71
/* Allow call to hash_free */
58
hash->free=0; /* Allow call to hash_free */
75
61
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 */
128
132
/* some helper functions */
131
This function is char* instead of unsigned char* as HPUX11 compiler can't
135
This function is char* instead of uchar* as HPUX11 compiler can't
132
136
handle inline functions that are not defined as native types
135
139
static inline char*
136
hash_key(const HASH *hash, const unsigned char *record, size_t *length,
140
hash_key(const HASH *hash, const uchar *record, size_t *length,
139
143
if (hash->get_key)
142
146
return (char*) record+hash->key_offset;
145
/* Calculate pos according to keys */
149
/* Calculate pos according to keys */
147
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)
149
153
if ((hashnr & (buffmax-1)) < maxlength) return (hashnr & (buffmax-1));
150
154
return (hashnr & ((buffmax >> 1) -1));
153
static uint32_t hash_rec_mask(const HASH *hash, HASH_LINK *pos,
154
uint32_t buffmax, uint32_t maxlength)
157
static uint hash_rec_mask(const HASH *hash, HASH_LINK *pos,
158
uint buffmax, uint maxlength)
157
unsigned char *key= (unsigned char*) hash_key(hash,pos->data,&length,0);
161
uchar *key= (uchar*) hash_key(hash,pos->data,&length,0);
158
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)
165
unsigned int rec_hashnr(HASH *hash,const unsigned char *record)
172
unsigned int rec_hashnr(HASH *hash,const uchar *record)
168
unsigned char *key= (unsigned char*) hash_key(hash,record,&length,0);
175
uchar *key= (uchar*) hash_key(hash,record,&length,0);
169
176
return calc_hash(hash,key,length);
173
unsigned char* hash_search(const HASH *hash, const unsigned char *key,
180
uchar* hash_search(const HASH *hash, const uchar *key, size_t length)
176
182
HASH_SEARCH_STATE state;
177
183
return hash_first(hash, key, length, &state);
181
187
Search after a record based on a key
184
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
187
unsigned char* hash_first(const HASH *hash, const unsigned char *key,
189
HASH_SEARCH_STATE *current_record)
193
uchar* hash_first(const HASH *hash, const uchar *key, size_t length,
194
HASH_SEARCH_STATE *current_record)
195
200
if (hash->records)
197
202
idx=hash_mask(calc_hash(hash,key,length ? length : hash->key_length),
198
hash->blength,hash->records);
203
hash->blength,hash->records);
201
206
pos= dynamic_element(&hash->array,idx,HASH_LINK*);
202
207
if (!hashcmp(hash,pos,key,length))
204
*current_record= idx;
209
*current_record= idx;
211
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 */
216
219
while ((idx=pos->next) != NO_RECORD);
266
267
Compare a key in a record to a whole key. Return 0 if identical
271
pos position of hash record to use in comparison
272
key key for comparison
272
pos position of hash record to use in comparison
273
key key for comparison
276
If length is 0, comparison is done using the length of the
277
record being compared against.
277
If length is 0, comparison is done using the length of the
278
record being compared against.
280
= 0 key of record == key
281
!= 0 key of record != key
281
= 0 key of record == key
282
!= 0 key of record != key
284
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,
287
288
size_t rec_keylength;
288
unsigned char *rec_key= (unsigned char*) hash_key(hash, pos->data,
289
uchar *rec_key= (uchar*) hash_key(hash,pos->data,&rec_keylength,1);
290
290
return ((length && length != rec_keylength) ||
291
my_strnncoll(hash->charset, rec_key, rec_keylength,
292
key, rec_keylength));
291
my_strnncoll(hash->charset, (const uchar*) rec_key, rec_keylength,
292
(const uchar*) key, rec_keylength));
296
/* Write a hash-key to the hash-index */
296
/* Write a hash-key to the hash-index */
298
bool my_hash_insert(HASH *info,const unsigned char *record)
298
bool my_hash_insert(HASH *info,const uchar *record)
302
uint32_t halfbuff,hash_nr,first_index;
303
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;
304
304
HASH_LINK *data,*empty,*gpos=NULL,*gpos2=NULL,*pos;
306
306
if (HASH_UNIQUE & info->flags)
308
unsigned char *key= (unsigned char*) hash_key(info, record, &idx, 1);
308
uchar *key= (uchar*) hash_key(info, record, &idx, 1);
309
309
if (hash_search(info, key, idx))
310
/* Duplicate entry */
310
return(true); /* Duplicate entry */
315
314
if (!(empty=(HASH_LINK*) alloc_dynamic(&info->array)))
315
return(true); /* No more memory */
319
317
data=dynamic_element(&info->array,0,HASH_LINK*);
320
318
halfbuff= info->blength >> 1;
322
idx= first_index= info->records-halfbuff;
323
/* If some records */
324
if (idx != info->records)
320
idx=first_index=info->records-halfbuff;
321
if (idx != info->records) /* If some records */
329
326
hash_nr=rec_hashnr(info,pos->data);
330
/* First loop; Check if ok */
332
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)
334
330
if (!(hash_nr & halfbuff))
336
/* Key will not move */
337
if (!(flag & LOWFIND))
341
flag=LOWFIND | HIGHFIND;
342
/* key shall be moved to the current empty position */
344
ptr_to_rec=pos->data;
345
/* This place is now free */
350
/* key isn't changed */
351
flag=LOWFIND | LOWUSED;
353
ptr_to_rec=pos->data;
358
if (!(flag & LOWUSED))
360
/* Change link of previous LOW-key */
361
gpos->data=ptr_to_rec;
362
gpos->next= (uint32_t) (pos-data);
363
flag= (flag & HIGHFIND) | (LOWFIND | LOWUSED);
366
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;
371
/* key will be moved */
372
if (!(flag & HIGHFIND))
374
flag= (flag & LOWFIND) | HIGHFIND;
375
/* key shall be moved to the last (empty) position */
376
gpos2 = empty; empty=pos;
377
ptr_to_rec2=pos->data;
381
if (!(flag & HIGHUSED))
383
/* Change link of previous hash-key and save */
384
gpos2->data=ptr_to_rec2;
385
gpos2->next=(uint32_t) (pos-data);
386
flag= (flag & LOWFIND) | (HIGHFIND | HIGHUSED);
389
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;
393
385
while ((idx=pos->next) != NO_RECORD);
438
430
/******************************************************************************
439
** Remove one record from hash-table. The record with the same record
441
** if there is a free-function it's called for record if found
442
*****************************************************************************/
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
******************************************************************************/
444
bool hash_delete(HASH *hash,unsigned char *record)
436
bool hash_delete(HASH *hash,uchar *record)
446
uint32_t blength,pos2,pos_hashnr,lastpos_hashnr,idx,empty_index;
438
uint blength,pos2,pos_hashnr,lastpos_hashnr,idx,empty_index;
447
439
HASH_LINK *data,*lastpos,*gpos,*pos,*pos3,*empty;
448
440
if (!hash->records)
521
508
pos->next=empty_index;
524
pop_dynamic(&hash->array);
511
VOID(pop_dynamic(&hash->array));
526
(*hash->free)((unsigned char*) record);
530
unsigned char *hash_element(HASH *hash,uint32_t idx)
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)
532
623
if (idx < hash->records)
533
624
return dynamic_element(&hash->array,idx,HASH_LINK*)->data;
537
} /* namespace drizzled */
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;