/* * Copyright (c) 2004, 2005 Sendmail, Inc. and its suppliers. * All rights reserved. * * By using this file, you agree to the terms and conditions set * forth in the LICENSE file which can be found at the top level of * the sendmail distribution. */ #include "sm/generic.h" SM_RCSID("@(#)$Id: bht.c,v 1.26 2006/05/02 17:13:41 ca Exp $") #include "sm/assert.h" #include "sm/error.h" #include "sm/memops.h" #include "sm/heap.h" #include "map.h" #include "sm/map.h" #include "sm/maps.h" #include "sm/mapc.h" #include "sm/mapclasses.h" #include "sm/bhtable.h" #include "sm/io.h" /* ToDo: implementation of map for binary hash table WARNING: the stored data must be NUL terminated strings Should this be a flag the determines whether the data stored is C strings (NUL terminated) or sm_str_S? Problem: lookup copies the data over into the output string. hence it doesn't help that the original data is returned by bhtable... NEED TO CLEANUP API and make it generic (flags?...) see include/sm/map.h split bht into create and open functions according to smX docs? */ /* static sm_map_open_F sm_bht_open; */ /* static sm_map_close_F sm_bht_close; */ static sm_map_alloc_F sm_bht_alloc; static sm_map_free_F sm_bht_free; static sm_map_locate_F sm_bht_locate; static sm_map_first_F sm_bht_first; static sm_map_next_F sm_bht_next; /* ** SM_BHT_DESTROY -- destroy map ** XXX more parameters... ** ** Parameters: ** map -- map ** flags -- flags ** ** Returns: ** usual sm_error code */ static sm_ret_T sm_bht_destroy(sm_map_P map, uint32_t flags) { sm_ret_T ret; sm_mapc_P mapc; bht_P db; SM_IS_MAP(map); mapc = map->sm_map_class; SM_IS_MAPC(mapc); ret = SM_SUCCESS; db = (bht_P) map->sm_map_db; if (db == NULL) return sm_error_perm(SM_EM_MAP, ENOENT); /* close BHT map */ bht_destroy(db, NULL, NULL); map->sm_map_db = NULL; return ret; } /* ** SM_BHT_SETOPT -- set options for map ** ** Parameters: ** map -- map ** ap -- options ** ** Returns: ** usual sm_error code */ static sm_ret_T sm_bht_setopt(sm_map_P map, va_list ap) { sm_ret_T ret; uint k, u; bht_P db; SM_IS_MAP(map); db = (bht_P) map->sm_map_db; if (db == NULL) return sm_error_perm(SM_EM_MAP, ENOENT); ret = SM_SUCCESS; for (;;) { k = va_arg(ap, uint); if (k == SMPO_END) break; switch (k) { case SMPO_MAX_ELEM: u = va_arg(ap, uint); db->bht_limit = u; break; case SMPO_HASH_NELEM: u = va_arg(ap, uint); db->bht_size = u; break; default: /* silently ignore bogus options? */ break; } } return ret; } /* ** SM_BHT_GETOPT -- get options for map ** ** Parameters: ** map -- map ** which -- which option? ** valp -- pointer to place where result should be stored ** ** Returns: ** usual sm_error code */ static sm_ret_T sm_bht_getopt(sm_map_P map, int which, void *valp) { sm_ret_T ret; bht_P db; SM_IS_MAP(map); db = (bht_P) map->sm_map_db; if (db == NULL) return sm_error_perm(SM_EM_MAP, ENOENT); /* ... */ ret = SM_SUCCESS; return ret; } /* ** SM_BHT_CLOSE -- close map ** XXX more parameters... ** ** Parameters: ** map -- map ** flags -- flags ** ** Returns: ** usual sm_error code */ static sm_ret_T sm_bht_close(sm_map_P map, uint32_t flags) { return SM_SUCCESS; } /* ** SM_BHT_CREATE -- create map ** ** Parameters: ** mapc -- map context ** type -- type of map ** flags -- flags for map ** map -- map ** ** Returns: ** usual sm_error code */ static sm_ret_T sm_bht_create(sm_mapc_P mapc, const sm_cstr_P type, uint32_t flags, sm_map_P map) { sm_ret_T ret; bht_P db; SM_IS_MAPC(mapc); SM_REQUIRE(map != NULL); db = NULL; ret = bht_create(&db); if (sm_is_success(ret)) { map->sm_map_db = db; map->sm_map_caps = SMMAP_CAPS_LTALL; } return ret; } /* ** SM_BHT_OPEN -- open map ** ** Parameters: ** mapc -- map context ** type -- type of map (currently ignored) ** flags -- flags for map (currently ignored) ** path -- path of map (currently ignored) ** mode -- open mode (currently ignored) ** map -- map ** ap -- additional argument ** ** Returns: ** usual sm_error code */ static sm_ret_T sm_bht_open(sm_mapc_P mapc, const sm_cstr_P type, uint32_t flags, const char *path, int mode, sm_map_P map, va_list ap) { sm_ret_T ret; uint k, u; bht_P db; sm_map_init_cb_F init_cb; void *init_ctx; SM_IS_MAPC(mapc); SM_REQUIRE(map != NULL); init_cb = NULL; init_ctx = NULL; db = map->sm_map_db; for (;;) { k = va_arg(ap, uint); if (k == SMPO_END) break; switch (k) { case SMPO_MAX_ELEM: u = va_arg(ap, uint); db->bht_limit = u; break; case SMPO_HASH_NELEM: u = va_arg(ap, uint); db->bht_size = u; break; case SMPO_INIT_CB: init_cb = va_arg(ap, sm_map_init_cb_F); init_ctx = va_arg(ap, void *); break; default: /* silently ignore bogus options? */ break; } } ret = bht_open(&db); if (sm_is_err(ret)) goto error; if (init_cb != NULL) { ret = init_cb(init_ctx); if (sm_is_err(ret)) goto error; } return SM_SUCCESS; error: if (db != NULL) { (void) bht_destroy(db, NULL, NULL); db = NULL; } return ret; } /* ** SM_BHT_LOOKUP -- lookup a key in BHT, return data if found ** ** Parameters: ** map -- map context ** flags -- flags ** key -- key ** data -- data (output) ** ** Returns: ** usual sm_error code */ static sm_ret_T sm_bht_lookup(sm_map_P map, uint32_t flags, sm_map_key_P key, sm_map_data_P data) { sm_ret_T ret; sm_mapc_P mapc; bht_P db; char *s; SM_IS_MAP(map); SM_IS_KEY(key); SM_IS_DATA(data); mapc = map->sm_map_class; SM_IS_MAPC(mapc); ret = SM_SUCCESS; db = (bht_P) map->sm_map_db; if (db == NULL) return sm_error_perm(SM_EM_MAP, ENOENT); /* XXX */ if (!SMMAP_IS_FL(map, SMMAP_FL_OPEN)) { /* map closed but can be reopened? */ if (mapc->sm_mapc_reopenf != NULL) ret = mapc->sm_mapc_reopenf(map, 0); else ret = sm_error_perm(SM_EM_MAP, SM_E_CLOSEDMAP); if (sm_is_err(ret)) return ret; } /* XXX WARNING: changes key inplace! */ if (SM_IS_FLAG(flags, SMMAP_FL_LWR_KEY)) sm_str2lower(key); s = bht_find(db, (const char *) sm_str_data(key), sm_str_getlen(key)); ret = (s == NULL) ? sm_error_perm(SM_EM_MAP, SM_E_NOTFOUND) : SM_SUCCESS; if (ret == SM_SUCCESS && data != NULL) { /* WARNING: must be NUL terminated string! */ ret = sm_str_scat(data, (const char *) s); if (sm_is_err(ret)) return ret; } return ret; } /* ** SM_BHT_ADD -- add key/data to BHT ** ** Parameters: ** map -- map context ** key -- key ** data -- data ** flags -- flags ** ** Returns: ** usual sm_error code */ static sm_ret_T sm_bht_add(sm_map_P map, sm_map_key_P key, sm_map_data_P data, uint flags) { sm_ret_T ret; sm_mapc_P mapc; bht_P db; bht_entry_P entry; SM_IS_MAP(map); SM_REQUIRE(key != NULL); SM_REQUIRE(data != NULL); mapc = map->sm_map_class; SM_IS_MAPC(mapc); ret = SM_SUCCESS; db = (bht_P) map->sm_map_db; if (db == NULL) return sm_error_perm(SM_EM_MAP, ENOENT); /* XXX */ ret = bht_add(db, (char *) sm_str_data(key), sm_str_getlen(key), sm_str_data(data), &entry); return ret; } /* ** SM_BHT_RM -- remove key/data from BHT ** ** Parameters: ** map -- map context ** key -- key ** ** Returns: ** usual sm_error code */ static sm_ret_T sm_bht_rm(sm_map_P map, sm_map_key_P key) { sm_mapc_P mapc; bht_P db; SM_IS_MAP(map); SM_REQUIRE(key != NULL); mapc = map->sm_map_class; SM_IS_MAPC(mapc); db = (bht_P) map->sm_map_db; if (db == NULL) return sm_error_perm(SM_EM_MAP, ENOENT); /* XXX */ bht_rm(db, (char *) sm_str_data(key), sm_str_getlen(key), NULL, NULL); return SM_SUCCESS; } /* ** SM_BHT_CLASS_CREATE -- create BHT map class ** ** Parameters: ** maps -- map system context ** ** Returns: ** usual sm_error code */ sm_ret_T sm_bht_class_create(sm_maps_P maps) { sm_ret_T ret; sm_mapc_P mapc; sm_cstr_P htype; #define BHT_TYPE "bhtable" ret = SM_SUCCESS; mapc = NULL; htype = sm_cstr_scpyn0((const uchar *)BHT_TYPE, strlen(BHT_TYPE)); if (htype == NULL) goto error; ret = sm_mapc_create(maps, htype, SMMAPC_FL_LCK_WR, sm_bht_create, sm_bht_open, sm_bht_close, NULL, /* XXX FIX! */ sm_bht_destroy, sm_bht_add, sm_bht_rm, sm_bht_alloc, sm_bht_free, sm_bht_lookup, sm_bht_locate, sm_bht_first, sm_bht_next, sm_bht_setopt, sm_bht_getopt, &mapc); SM_CSTR_FREE(htype); return ret; error: if (ret == SM_SUCCESS) ret = sm_error_temp(SM_EM_MAP, ENOMEM); /* cleanup mapc? */ return ret; }