|
|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: T g
Length: 10510 (0x290e)
Types: TextFile
Names: »gdbmstore.c«
└─⟦a05ed705a⟧ Bits:30007078 DKUUG GNU 2/12/89
└─⟦0befd2614⟧ »./gdbm-0.8.tar.Z«
└─⟦b993d2893⟧
└─⟦this⟧ »gdbm/gdbmstore.c«
/* gdbmstore.c - Add a new key/data pair to the database. */
/* GNU DBM - DataBase Manager (database subroutines) by Philip A. Nelson
Copyright (C) 1989 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
You may contact the author by:
e-mail: phil@wwu.edu
us-mail: Philip A. Nelson
Computer Science Department
Western Washington University
Bellingham, WA 98226
phone: (206) 676-3035
*************************************************************************/
#include <stdio.h>
#include <sys/file.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "gdbm.h"
#include "gdbmerrno.h"
extern gdbm_error gdbm_errno;
/* Add a new element to the database. CONTENT is keyed by KEY. The file on
disk is updated to reflect the structure of the new database before returning
from this procedure. A return value of 0 means no errors. A return value
of -1 means that the item was not stored in the data base because the
caller was not an official writer. */
int
gdbm_store (dbf, key, content)
dbm_file_info *dbf;
datum key;
datum content;
{
int new_hash_val; /* The new hash value. */
int elem_loc; /* The location in hash bucket. */
int file_adr; /* The address of new space in the file. */
int num_bytes; /* Used for error detection. */
int free_adr; /* For freeing a previous definition. */
int free_size; /* Size of the previous definition. */
/* First check to make sure this guy is a writer. */
if (dbf->read_write != DBM_WRITER)
{
gdbm_errno = READER_CANT_STORE;
return -1;
}
/* Look for the key in the file. A side effect loads the correct bucket. */
elem_loc = _dbm_findkey (dbf, key);
/* Compute the new hash value. */
new_hash_val = _dbm_hash (key);
/* Did we find the item? */
if (elem_loc != -1)
{
/* Just replace the data. */
free_adr = dbf->bucket->h_table[elem_loc].data_pointer;
free_size = dbf->bucket->h_table[elem_loc].key_size +
dbf->bucket->h_table[elem_loc].data_size;
}
else
{
/* This is a new item, prepare for the insert. */
free_size = 0;
while (dbf->bucket->count == dbf->header.bucket_elems)
{
/* Split the current bucket. */
split_current(dbf, new_hash_val);
}
/* Find space to insert into bucket and set elem_loc to that place. */
elem_loc = new_hash_val % dbf->header.bucket_elems;
while (dbf->bucket->h_table[elem_loc].hash_value != -1)
{ elem_loc = (elem_loc + 1) % dbf->header.bucket_elems; }
/* We now have another element in the bucket. Add the new information. */
dbf->bucket->count += 1;
dbf->bucket->h_table[elem_loc].hash_value = new_hash_val;
strncpy (dbf->bucket->h_table[elem_loc].key_start, key.dptr, SMALL);
}
/* Get space in the file for the data. */
file_adr = _dbm_alloc (dbf, key.dsize+content.dsize, FALSE);
/* Update current bucket data pointer and sizes. */
dbf->bucket->h_table[elem_loc].data_pointer = file_adr;
dbf->bucket->h_table[elem_loc].key_size = key.dsize;
dbf->bucket->h_table[elem_loc].data_size = content.dsize;
/* Write the data to the file. */
_dbm_update (dbf, 0x77777707, file_adr, key.dsize+content.dsize,
dbf->bucket_adr, elem_loc);
num_bytes = lseek (dbf->desc, file_adr, L_SET);
if (num_bytes != file_adr) _dbm_fatal (dbf, "lseek error");
num_bytes = write (dbf->desc, key.dptr, key.dsize);
if (num_bytes != key.dsize) _dbm_fatal (dbf, "write error");
num_bytes = write (dbf->desc, content.dptr, content.dsize);
if (num_bytes != content.dsize) _dbm_fatal (dbf, "write error");
/* Write current bucket to the file. */
num_bytes = lseek (dbf->desc, dbf->bucket_adr, L_SET);
if (num_bytes != dbf->bucket_adr) _dbm_fatal (dbf, "lseek error");
num_bytes = write (dbf->desc, dbf->bucket, dbf->header.bucket_size);
if (num_bytes != dbf->header.bucket_size) _dbm_fatal (dbf, "write error");
/* Make sure all is written to medium. */
fsync (dbf->desc);
/* Free previous space if this is a replacement. */
if (free_size != 0)
_dbm_free (dbf, free_adr, free_size);
/* Everything worked correctly. */
_dbm_end_update (dbf);
return 0;
}
/* Split the current bucket. This includes moving all items in the bucket to
a new bucket. This doesn't require any disk reads because all hash values
are stored in the buckets. Splitting the current bucket may require
doubling the size of the hash directory. */
static
split_current(dbf, next_insert)
dbm_file_info *dbf;
int next_insert;
{
hash_bucket *bucket[2]; /* Pointers to the new buckets. */
int index; /* Used in array indexing. */
int select; /* Used to index bucket during movement. */
int new_bits; /* The number of bits for the new buckets. */
int new_adr; /* File address of the new bucket. */
int elem_loc; /* Location in new bucket to put element. */
bucket_element *old_el; /* Pointer into the old bucket. */
int dir_start; /* Used in updating the directory. */
int dir_end;
int dir_adr; /* Address of the new directory. */
int dir_size; /* Size of the new directory. */
int old_adr; /* Address of the old directory. */
int old_size; /* Size of the old directory. */
int *new_dir; /* Pointer to the new directory. */
int num_bytes; /* For use with write. */
/* Allocate space for new buckets. */
bucket[0] = (hash_bucket *) malloc (dbf->header.bucket_size);
if (bucket[0] == NULL) _dbm_fatal (dbf, "malloc error");
bucket[1] = (hash_bucket *) malloc (dbf->header.bucket_size);
if (bucket[1] == NULL) _dbm_fatal (dbf, "malloc error");
new_bits = dbf->bucket->bucket_bits+1;
_dbm_new_bucket (dbf, bucket[0], new_bits);
_dbm_new_bucket (dbf, bucket[1], new_bits);
/* Double the directory size if necessary. */
if (dbf->header.dir_bits == dbf->bucket->bucket_bits)
{
dir_size = dbf->header.dir_size * 2;
dir_adr = _dbm_alloc (dbf, dir_size, TRUE);
new_dir = (int *) malloc (dir_size);
if (new_dir == NULL) _dbm_fatal (dbf, "malloc error");
for (index = 0; index < dbf->header.dir_size / sizeof (int); index++)
{
new_dir[2*index] = dbf->dir[index];
new_dir[2*index+1] = dbf->dir[index];
}
/* Write out new directory. */
num_bytes = lseek (dbf->desc, dir_adr, L_SET);
if (num_bytes != dir_adr) _dbm_fatal (dbf, "lseek error");
num_bytes = write (dbf->desc, new_dir, dir_size);
if (num_bytes != dir_size) _dbm_fatal (dbf, "write error");
fsync (dbf->desc);
/* Update header. */
old_adr = dbf->header.dir;
dbf->header.dir = dir_adr;
old_size = dbf->header.dir_size;
dbf->header.dir_size = dir_size;
dbf->header.dir_bits = new_bits;
num_bytes = lseek (dbf->desc, 0, L_SET);
if (num_bytes != 0) _dbm_fatal (dbf, "lseek error");
num_bytes = write (dbf->desc, &dbf->header, sizeof (dbm_file_header));
if (num_bytes != sizeof (dbm_file_header)) _dbm_fatal (dbf, "write error");
fsync (dbf->desc);
/* Now update dbf. */
dbf->bucket_dir *= 2;
free (dbf->dir);
dbf->dir = new_dir;
_dbm_free (dbf, old_adr, old_size);
}
/* Copy all elements in dbf->bucket into the new buckets. */
for (index = 0; index < dbf->header.bucket_elems; index++)
{
old_el = & (dbf->bucket->h_table[index]);
select = (old_el->hash_value >> (31-new_bits)) & 1;
elem_loc = old_el->hash_value % dbf->header.bucket_elems;
while (bucket[select]->h_table[elem_loc].hash_value != -1)
elem_loc = (elem_loc + 1) % dbf->header.bucket_elems;
bucket[select]->h_table[elem_loc] = *old_el;
bucket[select]->count += 1;
}
/* Update the directory. Current bucket address for bucket[0]. No need to
change entries in the directory for bucket[0]. Get a new file address
for bucket[1]. We need to change the entries in the directory to point
to the bucket[1]. */
new_adr = _dbm_alloc (dbf, dbf->header.bucket_size, TRUE);
dir_start = (dbf->bucket_dir >> (dbf->header.dir_bits - new_bits)) | 1;
dir_end = (dir_start + 1) << (dbf->header.dir_bits - new_bits);
dir_start = dir_start << (dbf->header.dir_bits - new_bits);
for (index = dir_start; index < dir_end; index++)
dbf->dir[index] = new_adr;
/* Write the new buckets and directory to disk. */
_dbm_update (dbf, 0x77777708, new_adr, dbf->header.bucket_size,
dbf->bucket_adr, dbf->bucket_dir);
num_bytes = lseek (dbf->desc, new_adr, L_SET);
if (num_bytes != new_adr ) _dbm_fatal (dbf, "lseek error");
num_bytes = write (dbf->desc, bucket[1], dbf->header.bucket_size);
if (num_bytes != dbf->header.bucket_size ) _dbm_fatal (dbf, "write error");
num_bytes = lseek (dbf->desc, dbf->bucket_adr, L_SET);
if (num_bytes != dbf->bucket_adr ) _dbm_fatal (dbf, "lseek error");
num_bytes = write (dbf->desc, bucket[0], dbf->header.bucket_size);
if (num_bytes != dbf->header.bucket_size ) _dbm_fatal (dbf, "write error");
num_bytes = lseek (dbf->desc, dbf->header.dir, L_SET);
if (num_bytes != dbf->header.dir ) _dbm_fatal (dbf, "lseek error");
num_bytes = write (dbf->desc, dbf->dir, dbf->header.dir_size);
if (num_bytes != dbf->header.dir_size ) _dbm_fatal (dbf, "write error");
fsync (dbf->desc);
_dbm_end_update (dbf);
/* Make the correct new bucket the current one and free the other space. */
dbf->bucket_dir = next_insert >> (31-dbf->header.dir_bits);
dbf->bucket_adr = dbf->dir[dbf->bucket_dir];
free (dbf->bucket);
if (dbf->bucket_adr == new_adr)
{
dbf->bucket = bucket[1];
free(bucket[0]);
}
else
{
dbf->bucket = bucket[0];
free(bucket[1]);
}
}