diff -r ae203b2c56a6 configure.ac --- a/configure.ac Thu May 12 12:41:25 2016 -0700 +++ b/configure.ac Tue May 24 12:19:45 2016 +0000 @@ -855,6 +855,7 @@ AC_ARG_WITH(qdbm, AS_HELP_STRING([--without-qdbm],[Don't use qdbm even if it is available])) AC_ARG_WITH(gdbm, AS_HELP_STRING([--without-gdbm],[Don't use gdbm even if it is available])) AC_ARG_WITH(bdb, AS_HELP_STRING([--with-bdb@<:@=DIR@:>@],[Use BerkeleyDB4 if gdbm is not available])) +AC_ARG_WITH(lmdb, AS_HELP_STRING([--with-lmdb@<:@=DIR@:>@],[Use LMDB if gdbm is not available])) db_found=no if test x$enable_hcache = xyes @@ -899,6 +900,15 @@ db_requested=bdb fi fi + if test -n "$with_lmdb" && test "$with_lmdb" != "no" + then + if test "$db_requested" != "auto" + then + AC_MSG_ERROR([more than one header cache engine requested.]) + else + db_requested=lmdb + fi + fi dnl -- Tokyo Cabinet -- if test "$with_tokyocabinet" != "no" \ @@ -1042,6 +1052,34 @@ fi fi + dnl -- LMDB -- + if test x$with_lmdb != xno && test $db_found = no \ + && test "$db_requested" = auto -o "$db_requested" = lmdb + then + if test "$with_lmdb" != "yes" + then + CPPFLAGS="$CPPFLAGS -I$with_lmdb/include" + LDFLAGS="$LDFLAGS -L$with_lmdb/lib" + fi + saved_LIBS="$LIBS" + LIBS="$LIBS -llmdb" + AC_CACHE_CHECK(for mdb_env_create, ac_cv_mdbenvcreate,[ + ac_cv_mdbenvcreate=no + AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include ]], [[mdb_env_create(0);]])],[ac_cv_mdbenvcreate=yes],[]) + ]) + LIBS="$saved_LIBS" + if test "$ac_cv_mdbenvcreate" = yes + then + AC_DEFINE(HAVE_LMDB, 1, [LMDB Support]) + MUTTLIBS="$MUTTLIBS -llmdb" + db_found=lmdb + fi + if test "$db_requested" != auto && test "$db_found" != "$db_requested" + then + AC_MSG_ERROR([LMDB could not be used. Check config.log for details.]) + fi + fi + if test $db_found = no then AC_MSG_ERROR([You need Tokyo Cabinet, QDBM, GDBM or Berkeley DB4 for hcache]) diff -r ae203b2c56a6 hcache.c --- a/hcache.c Thu May 12 12:41:25 2016 -0700 +++ b/hcache.c Tue May 24 12:19:45 2016 +0000 @@ -32,6 +32,9 @@ #include #elif HAVE_DB4 #include +#elif HAVE_LMDB +#define LMDB_DB_SIZE (1024 * 1024 * 1024) +#include #endif #include @@ -83,6 +86,14 @@ static void mutt_hcache_dbt_init(DBT * dbt, void *data, size_t len); static void mutt_hcache_dbt_empty_init(DBT * dbt); +#elif HAVE_LMDB +struct header_cache +{ + MDB_env *env; + MDB_dbi db; + char *folder; + unsigned int crc; +}; #endif typedef union @@ -732,6 +743,12 @@ #elif HAVE_DB4 DBT key; DBT data; +#elif HAVE_LMDB + MDB_val key; + MDB_val data; + MDB_txn *txn; + size_t folderlen; + int rc; #endif if (!h) @@ -748,6 +765,41 @@ h->db->get(h->db, NULL, &key, &data, 0); return data.data; +#elif HAVE_LMDB + strncpy(path, h->folder, sizeof (path)); + safe_strcat(path, sizeof (path), filename); + + folderlen = strlen(h->folder); + ksize = folderlen + keylen(path + folderlen); + key.mv_data = (char *)path; + key.mv_size = ksize; + data.mv_data = NULL; + data.mv_size = 0; + rc = mdb_txn_begin(h->env, NULL, MDB_RDONLY, &txn); + if (rc != MDB_SUCCESS) { + fprintf(stderr, "txn_begin: %s\n", mdb_strerror(rc)); + return NULL; + } + rc = mdb_get(txn, h->db, &key, &data); + if (rc == MDB_NOTFOUND) { + mdb_txn_abort(txn); + return NULL; + } + if (rc != MDB_SUCCESS) { + fprintf(stderr, "mdb_get: %s\n", mdb_strerror(rc)); + mdb_txn_abort(txn); + return NULL; + } + /* Caller frees the data we return, so I MUST make a copy of it */ + + char *d = malloc(data.mv_size); + if (d) { + memcpy(d, data.mv_data, data.mv_size); + } + mdb_txn_abort(txn); + + return d; + #else strncpy(path, h->folder, sizeof (path)); safe_strcat(path, sizeof (path), filename); @@ -813,6 +865,12 @@ #elif HAVE_DB4 DBT key; DBT databuf; +#elif HAVE_LMDB + MDB_val key; + MDB_val databuf; + MDB_txn *txn; + size_t folderlen; + int rc; #endif if (!h) @@ -831,6 +889,29 @@ databuf.ulen = dlen; return h->db->put(h->db, NULL, &key, &databuf, 0); +#elif HAVE_LMDB + folderlen = strlen(h->folder); + strncpy(path, h->folder, sizeof (path)); + safe_strcat(path, sizeof (path), filename); + ksize = folderlen + keylen(path + folderlen); + + key.mv_data = (char *)path; + key.mv_size = ksize; + databuf.mv_data = data; + databuf.mv_size = dlen; + rc = mdb_txn_begin(h->env, NULL, 0, &txn); + if (rc != MDB_SUCCESS) { + fprintf(stderr, "txn_begin: %s\n", mdb_strerror(rc)); + return rc; + } + rc = mdb_put(txn, h->db, &key, &databuf, 0); + if (rc != MDB_SUCCESS) { + fprintf(stderr, "mdb_put: %s\n", mdb_strerror(rc)); + mdb_txn_abort(txn); + return rc; + } + rc = mdb_txn_commit(txn); + return rc; #else strncpy(path, h->folder, sizeof (path)); safe_strcat(path, sizeof (path), filename); @@ -1134,6 +1215,93 @@ mutt_hcache_dbt_init(&key, (void *) filename, keylen(filename)); return h->db->del(h->db, NULL, &key, 0); } +#elif HAVE_LMDB + +static int +hcache_open_lmdb (struct header_cache* h, const char* path) +{ + MDB_txn *txn; + int rc; + + rc = mdb_env_create(&h->env); + if (rc != MDB_SUCCESS) { + fprintf(stderr, "hcache_open_lmdb: mdb_env_create: %s", mdb_strerror(rc)); + return -1; + } + + mdb_env_set_mapsize(h->env, LMDB_DB_SIZE); + + rc = mdb_env_open(h->env, path, MDB_NOSUBDIR, 0644); + if (rc != MDB_SUCCESS) { + fprintf(stderr, "hcache_open_lmdb: mdb_env_open: %s", mdb_strerror(rc)); + goto fail_env; + } + + rc = mdb_txn_begin(h->env, NULL, 0, &txn); + if (rc != MDB_SUCCESS) { + fprintf(stderr, "hcache_open_lmdb: mdb_txn_begin: %s", mdb_strerror(rc)); + goto fail_env; + } + + rc = mdb_dbi_open(txn, NULL, MDB_CREATE, &h->db); + if (rc != MDB_SUCCESS) { + fprintf(stderr, "hcache_open_lmdb: mdb_dbi_open: %s", mdb_strerror(rc)); + goto fail_dbi; + } + + mdb_txn_commit(txn); + return 0; + +fail_dbi: + mdb_txn_abort(txn); + +fail_env: + mdb_env_close(h->env); + return -1; +} + +void +mutt_hcache_close(header_cache_t *h) +{ + if (!h) + return; + + mdb_env_close(h->env); + FREE (&h->folder); + FREE (&h); +} + +int +mutt_hcache_delete(header_cache_t *h, const char *filename, + size_t(*keylen) (const char *fn)) +{ + MDB_val key; + MDB_txn *txn; + int rc; + + if (!h) + return -1; + + if (filename[0] == '/') + filename++; + + key.mv_data = (char *)filename; + key.mv_size = strlen(filename); + rc = mdb_txn_begin(h->env, NULL, 0, &txn); + if (rc != MDB_SUCCESS) { + fprintf(stderr, "txn_begin: %s\n", mdb_strerror(rc)); + return rc; + } + rc = mdb_del(txn, h->db, &key, NULL); + if (rc != MDB_SUCCESS) { + fprintf(stderr, "mdb_del: %s\n", mdb_strerror(rc)); + mdb_txn_abort(txn); + return rc; + } + + mdb_txn_commit(txn); + return rc; +} #endif header_cache_t * @@ -1151,6 +1319,8 @@ hcache_open = hcache_open_gdbm; #elif HAVE_DB4 hcache_open = hcache_open_db4; +#elif HAVE_LMDB + hcache_open = hcache_open_lmdb; #endif /* Calculate the current hcache version from dynamic configuration */ @@ -1188,7 +1358,11 @@ hcachever = digest.intval; } +#if HAVE_LMDB + h->db = 0; +#else h->db = NULL; +#endif h->folder = get_foldername(folder); h->crc = hcachever; @@ -1223,6 +1397,11 @@ { return DB_VERSION_STRING; } +#elif HAVE_LMDB +const char *mutt_hcache_backend (void) +{ + return "lmdb " MDB_VERSION_STRING; +} #elif HAVE_GDBM const char *mutt_hcache_backend (void) {