(1)int gdbm_store(dbf,key,content,flags)函数。向数据库中存入新的关键字/数据。key为关键字,content为数据项。要注意的是gdbm实现时对存储数据大小没有限制。操作流程:
1)若数据库不可写或者要存的数据为空,则返回-1;
2)调用_gdbm_findkey在数据库中查找这个关键字,若找到,当flags为GDBM_REPLACE时用新数据项覆盖关键字原来关联的数据,当flags为GDBM_INSERT时,则于数据已存在而不可插入,返回1;
3)若没找到这个关键字,则扫描桶缓存,找到一个未存放数据的桶元素,把关键字/数据存入这个桶元素中去;
4)最后把关键和数据写入文件,调用_gdbm_end_update把缓存中的所有更改也写入文件。
/* 确认数据库的打开标志是可写的 */
if (dbf->read_write == GDBM_READER)
{
gdbm_errno = GDBM_READER_CANT_STORE;
return -1;
}
/* 检查不合法的数据值。一个NULL dptr域是不合法的,因为被一个搜索过程返回了NULL dptr,表明这是一个错误 */
if ((key.dptr == NULL) || (content.dptr == NULL))
{
gdbm_errno = GDBM_ILLEGAL_DATA;
return -1;
}
/* 初始化gdbm_errno变量 */
gdbm_errno = GDBM_NO_ERROR;
/* 在文件中查找我们给出的key,返回其中桶中所处的位置。这也会载入正确的桶,并且计算key的哈希值 */
elem_loc = _gdbm_findkey (dbf, key, &temp, &new_hash_val); /* new_hash_val会返回关键字的哈希值 */
/* 初始化 */
file_adr = 0;
new_size = key.dsize + content.dsize;
/* 如果找到这个key */
if (elem_loc != -1)
{
if (flags == GDBM_REPLACE) /* 表示用新数据覆盖关键字关联的原数据 */
{
/* 找到数据坎的地址和长度 */
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; /* 获取原来的关键字/数据长度 */
if (free_size != new_size) /* 如果不等,则释放这段空间,以便能重新分配 */
{
_gdbm_free (dbf, free_adr, free_size);
}
else
{
/* 只需重新使用同样的地址 */
file_adr = free_adr;
}
}
else /* 找到了这个key,但操作标志不是GDBM_REPLACE,因此出错 */
{
gdbm_errno = GDBM_CANNOT_REPLACE;
return 1;
}
}
/* 分配新的磁盘空间并返回其地址(当前桶的可用块是首先要使用的地方) */
if (file_adr == 0)
{
file_adr = _gdbm_alloc (dbf, new_size);
}
/* 如果没有找到这个key,说明要存入的是一个新的关键字/数据,我们需要做更多工作 */
if (elem_loc == -1)
{
if (dbf->bucket->count == dbf->header->bucket_elems) /* 如果当前桶中元素个数达到最大 */
{
/* 分裂当前桶 */
_gdbm_split_bucket (dbf, new_hash_val);
}
/* 找到新的未存放数据的桶元素,把关键字/数据存入这个桶元素中,并设置elem_loc为这个桶元素的位置 */
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; }
/* 现在有了一个可用桶元素,把关键字/数据存入这个桶元素中 */
dbf->bucket->count += 1;
dbf->bucket->h_table[elem_loc].hash_value = new_hash_val;
bcopy (key.dptr, dbf->bucket->h_table[elem_loc].key_start,
(SMALL < key.dsize ? SMALL : key.dsize)); /* 把数据复制到桶元素中 */
}
/* 存入之后,要更新当前桶元素的数据指针和数据大小 */
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;
/* 把要存的关键字和数据项写入文件 */
file_pos = lseek (dbf->desc, file_adr, L_SET);
if (file_pos != file_adr) _gdbm_fatal (dbf, "lseek error");
num_bytes = write (dbf->desc, key.dptr, key.dsize);
if (num_bytes != key.dsize) _gdbm_fatal (dbf, "write error");
num_bytes = write (dbf->desc, content.dptr, content.dsize);
if (num_bytes != content.dsize) _gdbm_fatal (dbf, "write error");
/* 当前桶已经改变了 */
dbf->cache_entry->ca_changed = TRUE;
dbf->bucket_changed = TRUE;
/* 把需要写回的都写回磁盘 */
_gdbm_end_update (dbf); /* 把内存中的所有改变(桶缓存、散列目录表、文件头)写入磁盘 */
return 0;
}
(2)datum gdbm_fetch(dbf,key)函数。查找给定关键字,返回其所关联的数据。返回的datum结构中的指针域会指向一个动态分配的内存块(存放了数据),而不是磁盘文件上的地址。程序调用_gdbm_findkey在数据库中查找关键字,若找到,则复制其关联的数据到datum结构中,并返回这个datum结构。
(3)int gdbm_delete(dbf,key)函数。从数据库dbf中删除给定的关键字和其关联的数据。在从本过程返回之前,磁盘上的文件会被更新以反映新数据库文件的结构。操作流程:
1)若数据库不可写,则返回-1;
2)调用_gdbm_findkey在数据库中查找这个关键字,根据返回的位置,保存关键字所在的桶元素,然后删除这个桶元素;
3)把后面的所有存放了数据的桶元素前移,以确保它们能被找到;
4)释放关键字数据的文件空间;
5)清除当前缓存项中的数据,然后调用_gdbm_end_update完成数据库文件的所有更新。
{
dbf->bucket->h_table[last_loc] = dbf->bucket->h_table[elem_loc];
dbf->bucket->h_table[elem_loc].hash_value = -1;
last_loc = elem_loc;
}
elem_loc = (elem_loc + 1) % dbf->header->bucket_elems;
}
/* 释放关键字数据的文件空间 */
free_adr = elem.data_pointer;
free_size = elem.key_size + elem.data_size;
_gdbm_free (dbf, free_adr, free_size); /* 释放空间 */
/* 设置标识 */
dbf->bucket_changed = TRUE;
/* 清除当前缓存项中的数据 */
if (dbf->cache_entry->ca_data.dptr != NULL)
{
free (dbf->cache_entry->ca_data.dptr);
dbf->cache_entry->ca_data.dptr = NULL;
}
dbf->cache_entry->ca_data.hash_val = -1;
dbf->cache_entry->ca_data.key_size = 0;
dbf->cache_entry->ca_data.elem_loc = -1;
/*写回所有的更改 */
_gdbm_end_update (dbf);
return 0;
}