4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
25 * Copyright 1988-2002 Sun Microsystems, Inc. All rights reserved.
26 * Use is subject to license terms.
29 #pragma ident "%Z%%M% %I% %E% SMI"
33 #include "db_headers.h"
35 #include "db_pickle.h"
40 static int hashsizes
[] = { /* hashtable sizes */
52 // prevents wrap around numbers from being passed
53 #define CALLOC_LIMIT 536870911
55 /* Constructor: creates empty index. */
67 /* Destructor: deletes index, including all associated db_index_entry. */
70 WRITELOCKV(this, "w db_index::~db_index");
75 /* Get rid of table and all associated entries, and reset counters */
79 db_index_entry
* curr
, *n
;
82 WRITELOCKV(this, "w db_index::reset");
83 /* Add sanity test in case table was corrupted */
85 for (i
= 0; i
< table_size
; i
++) { // go through table
87 while (curr
!= NULL
) { // go through bucket
88 n
= curr
->getnextentry();
95 delete tab
; // get rid of table itself
98 table_size
= count
= 0;
99 WRITEUNLOCKV(this, "wu db_index::reset");
104 * Initialize index according to the specification of the key descriptor
105 * Currently, only affects case_insens flag of index.
108 db_index::init(db_key_desc
* k
)
110 WRITELOCKV(this, "w db_index::init");
111 if ((k
->key_flags
)&DB_KEY_CASE
)
113 WRITEUNLOCKV(this, "wu db_index::init");
116 /* Returns the next size to use for the hash table */
118 get_next_hashsize(long unsigned oldsize
)
120 long unsigned newsize
= 0, n
;
122 newsize
= hashsizes
[0];
124 for (n
= 0; newsize
= hashsizes
[n
++]; )
125 if (oldsize
== newsize
) {
126 newsize
= hashsizes
[n
]; /* get next size */
130 newsize
= oldsize
* 2 + 1; /* just double */
136 * Grow the current hashtable upto the next size.
137 * The contents of the existing hashtable is copied to the new one and
138 * relocated according to its hashvalue relative to the new size.
139 * Old table is deleted after the relocation.
144 long unsigned oldsize
= table_size
, i
;
145 db_index_entry_p
* oldtab
= tab
;
147 WRITELOCKV(this, "w db_index::grow");
148 table_size
= get_next_hashsize(table_size
);
152 fprintf(ddt
, "savehash GROWING to %d\n", table_size
);
155 if (table_size
> CALLOC_LIMIT
) {
156 table_size
= oldsize
;
158 "wu db_index::grow: table size exceeds calloc limit");
159 FATAL("db_index::grow: table size exceeds calloc limit",
163 if ((tab
= (db_index_entry_p
*)
164 calloc((unsigned int) table_size
,
165 sizeof (db_index_entry_p
))) == NULL
) {
166 tab
= oldtab
; // restore previous table info
167 table_size
= oldsize
;
169 "wu db_index::grow: cannot allocate space");
170 FATAL("db_index::grow: cannot allocate space", DB_MEMORY_LIMIT
);
173 if (oldtab
!= NULL
) { // must transfer contents of old to new
174 for (i
= 0; i
< oldsize
; i
++) {
175 oldtab
[i
]->relocate(tab
, table_size
);
177 delete oldtab
; // delete old hashtable
179 WRITEUNLOCKV(this, "wu db_index::grow");
183 * Look up given index value in hashtable.
184 * Return pointer to db_index_entries that match the given value, linked
185 * via the 'next_result' pointer. Return in 'how_many_found' the size
186 * of this list. Return NULL if not found.
189 db_index::lookup(item
*index_value
, long *how_many_found
,
190 db_table
*table
, bool_t checkTTL
)
192 register unsigned long hval
;
193 unsigned long bucket
;
196 READLOCK(this, NULL
, "r db_index::lookup");
197 if (index_value
== NULL
|| table_size
== 0 || tab
== NULL
) {
198 READUNLOCK(this, NULL
, "ru db_index::lookup");
201 hval
= index_value
->get_hashval(case_insens
);
202 bucket
= hval
% table_size
;
204 db_index_entry_p fst
= tab
[bucket
];
207 ret
= fst
->lookup(case_insens
, hval
,
208 index_value
, how_many_found
);
212 if (ret
!= NULL
&& checkTTL
&& table
!= NULL
) {
213 if (!table
->cacheValid(ret
->getlocation()))
217 READUNLOCK(this, ret
, "ru db_index::lookup");
222 * Remove the entry with the given index value and location 'recnum'.
223 * If successful, return DB_SUCCESS; otherwise DB_NOTUNIQUE if index_value
224 * is null; DB_NOTFOUND if entry is not found.
225 * If successful, decrement count of number of entries in hash table.
228 db_index::remove(item
* index_value
, entryp recnum
)
230 register unsigned long hval
;
231 unsigned long bucket
;
232 register db_index_entry
*fst
;
235 if (index_value
== NULL
)
236 return (DB_NOTUNIQUE
);
237 WRITELOCK(this, DB_LOCK_ERROR
, "w db_index::remove");
238 if (table_size
== 0 || tab
== NULL
) {
239 WRITEUNLOCK(this, DB_NOTFOUND
, "wu db_index::remove");
240 return (DB_NOTFOUND
);
242 hval
= index_value
->get_hashval(case_insens
);
244 bucket
= hval
% table_size
;
249 else if (fst
->remove(&tab
[bucket
], case_insens
, hval
, index_value
,
255 WRITEUNLOCK(this, ret
, "wu db_index::remove");
260 * Add a new index entry with the given index value and location 'recnum'.
261 * Return DB_NOTUNIQUE, if entry with identical index_value and recnum
262 * already exists. If entry is added, return DB_SUCCESS.
263 * Increment count of number of entries in index table and grow table
264 * if number of entries equals size of table.
265 * Note that a copy of index_value is made for new entry.
268 db_index::add(item
* index_value
, entryp recnum
)
270 register unsigned long hval
;
272 if (index_value
== NULL
)
273 return (DB_NOTUNIQUE
);
275 hval
= index_value
->get_hashval(case_insens
);
277 WRITELOCK(this, DB_LOCK_ERROR
, "w db_index::add");
278 if (tab
== NULL
) grow();
280 db_index_entry_p fst
, newbucket
;
281 unsigned long bucket
;
282 bucket
= hval
%table_size
;
284 if (fst
== NULL
) { /* Empty bucket */
285 if ((newbucket
= new db_index_entry(hval
, index_value
,
286 recnum
, tab
[bucket
])) == NULL
) {
287 WRITEUNLOCK(this, DB_MEMORY_LIMIT
,
289 FATAL3("db_index::add: cannot allocate space",
290 DB_MEMORY_LIMIT
, DB_MEMORY_LIMIT
);
292 tab
[bucket
] = newbucket
;
293 } else if (fst
->add(&tab
[bucket
], case_insens
,
294 hval
, index_value
, recnum
)) {
297 WRITEUNLOCK(this, DB_NOTUNIQUE
, "wu db_index::add");
298 return (DB_NOTUNIQUE
);
301 /* increase hash table size if number of entries equals table size */
302 if (++count
> table_size
)
305 WRITEUNLOCK(this, DB_SUCCESS
, "wu db_index::add");
309 /* ************************* pickle_index ********************* */
311 /* Does the actual writing to/from file specific for db_index structure. */
313 transfer_aux(XDR
* x
, pptr ip
)
315 return (xdr_db_index(x
, (db_index
*) ip
));
318 class pickle_index
: public pickle_file
{
320 pickle_index(char *f
, pickle_mode m
) : pickle_file(f
, m
) {}
322 /* Transfers db_index structure pointed to by dp to/from file. */
323 int transfer(db_index
* dp
)
324 { return (pickle_file::transfer((pptr
) dp
, &transfer_aux
)); }
327 /* Dumps this index to named file. */
329 db_index::dump(char *file
)
332 pickle_index
f(file
, PICKLE_WRITE
);
334 WRITELOCK(this, -1, "w db_index::dump");
335 int status
= f
.transfer(this);
338 ret
= -1; /* cannot open for write */
341 WRITEUNLOCK(this, ret
, "wu db_index::dump");
345 * Constructor: creates index by loading it from the specified file.
346 * If loading fails, creates empty index.
348 db_index::db_index(char *file
)
350 pickle_index
f(file
, PICKLE_READ
);
352 table_size
= count
= 0;
354 /* load new hashbuf */
355 if (f
.transfer(this) < 0) {
356 /* Load failed; reset. */
358 table_size
= count
= 0;
366 * Return in 'tsize' the table_size, and 'tcount' the number of entries
370 db_index::stats(long *tsize
, long *tcount
)
372 READLOCKV(this, "r db_index::stats");
375 READUNLOCKV(this, "ru db_index::stats");
378 /* Print all entries in the table. */
384 READLOCKV(this, "r db_index::print");
385 /* Add sanity check in case table corrupted */
387 for (i
= 0; i
< table_size
; i
++) {
392 READUNLOCKV(this, "ru db_index::print");
396 * Moves an index from an xdr index. Upon completion, original index's tab
401 db_index::move_xdr_db_index(db_index
*orig
)
403 table_size
= orig
->table_size
;
404 orig
->table_size
= 0;
407 case_insens
= orig
->case_insens
;