[PATCH] Add tkrzw hcache support.

"Kevin J. McCarthy" <[email protected]>
Newsgroups gmane.mail.mutt.devel
Message-ID <[email protected]>
The author is the same as KyotoCabinet, so the API is pretty similar
and it was fairly easy to add.

The only difference is with compression.  KyotoCabinet allowed a
generic "c" parameter to enable compression.  Tkrzw seems to require
specifying a specific implementation, and those implementations are
enabled during the configuration and build of tkrzw.

Since mutt doesn't know what's available, I've not enabled
OPTHCACHECOMPRESS support.
---
 INSTALL      |  2 +-
 configure.ac | 35 +++++++++++++++++++++-
 hcache.c     | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 120 insertions(+), 2 deletions(-)

diff --git a/INSTALL b/INSTALL
index 445adb3f..bacd21c3 100644
--- a/INSTALL
+++ b/INSTALL
@@ -137,7 +137,7 @@ options are:
 --enable-hcache
         Enable header caching support.  If no backend library is
         specified via a --with option (e.g. --with-kyotocabinet), Mutt
-        will scan in the order: kyotocabinet, tokyocabinet, lmdb,
+        will scan in the order: tkrzw, kyotocabinet, tokyocabinet, lmdb,
         qdbm, gdbm, bdb.  To skip scanning one or more of these
         libraries, use the corresponding --without option.
 
diff --git a/configure.ac b/configure.ac
index 397e2086..074f002e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1014,6 +1014,7 @@ dnl -- start cache --
 db_found=no
 db_requested=auto
 AC_ARG_ENABLE(hcache, AS_HELP_STRING([--enable-hcache],[Enable header caching]))
+AC_ARG_WITH(tkrzw, AS_HELP_STRING([--with-tkrzw@<:@=DIR@:>@],[Use tkrzw hcache backend]))
 AC_ARG_WITH(kyotocabinet, AS_HELP_STRING([--with-kyotocabinet@<:@=DIR@:>@],[Use kyotocabinet hcache backend]))
 AC_ARG_WITH(tokyocabinet, AS_HELP_STRING([--with-tokyocabinet@<:@=DIR@:>@],[Use tokyocabinet hcache backend]))
 AC_ARG_WITH(lmdb, AS_HELP_STRING([--with-lmdb@<:@=DIR@:>@],[Use lmdb hcache backend]))
@@ -1032,6 +1033,15 @@ then
 
     need_md5="yes"
 
+    if test -n "$with_tkrzw" && test "$with_tkrzw" != "no"
+    then
+      if test "$db_requested" != "auto"
+      then
+        AC_MSG_ERROR([more than one header cache engine requested.])
+      else
+        db_requested=tkrzw
+      fi
+    fi
     if test -n "$with_kyotocabinet" && test "$with_kyotocabinet" != "no"
     then
       if test "$db_requested" != "auto"
@@ -1087,6 +1097,29 @@ then
       fi
     fi
 
+    dnl -- Tkrzw --
+    if test x$with_tkrzw != xno && test $db_found = no \
+            && test "$db_requested" = auto -o "$db_requested" = tkrzw
+    then
+      if test -n "$with_tkrzw" && test "$with_tkrzw" != "yes"
+      then
+        CPPFLAGS="$CPPFLAGS -I$with_tkrzw/include"
+        LDFLAGS="$LDFLAGS -L$with_tkrzw/lib"
+      fi
+
+      AC_CHECK_HEADER(tkrzw_langc.h,
+      AC_CHECK_LIB(tkrzw, tkrzw_dbm_open,
+        [MUTTLIBS="$MUTTLIBS -ltkrzw"
+         AC_DEFINE(HAVE_TKRZW, 1, [Tkrzw Support])
+         db_found=tkrzw],
+        [CPPFLAGS="$OLDCPPFLAGS"
+         LDFLAGS="$OLDLDFLAGS"]))
+      if test "$db_requested" != auto && test "$db_found" != "$db_requested"
+      then
+        AC_MSG_ERROR([Tkrzw could not be used. Check config.log for details.])
+      fi
+    fi
+
     dnl -- Kyoto Cabinet --
     if test x$with_kyotocabinet != xno && test $db_found = no \
             && test "$db_requested" = auto -o "$db_requested" = kc
@@ -1259,7 +1292,7 @@ then
 
     if test $db_found = no
     then
-        AC_MSG_ERROR([You need Kyoto Cabinet, Tokyo Cabinet, LMDB, QDBM, GDBM, or BDB for hcache])
+        AC_MSG_ERROR([You need Tkrzw, Kyoto Cabinet, Tokyo Cabinet, LMDB, QDBM, GDBM, or BDB for hcache])
     fi
 fi
 dnl -- end cache --
diff --git a/hcache.c b/hcache.c
index d006055e..dc52aba0 100644
--- a/hcache.c
+++ b/hcache.c
@@ -30,6 +30,8 @@
 #include <tcbdb.h>
 #elif HAVE_KC
 #include <kclangc.h>
+#elif HAVE_TKRZW
+#include <tkrzw_langc.h>
 #elif HAVE_GDBM
 #include <gdbm.h>
 #elif HAVE_DB4
@@ -77,6 +79,13 @@ struct header_cache
   char *folder;
   unsigned int crc;
 };
+#elif HAVE_TKRZW
+struct header_cache
+{
+  TkrzwDBM *db;
+  char *folder;
+  unsigned int crc;
+};
 #elif HAVE_GDBM
 struct header_cache
 {
@@ -841,6 +850,8 @@ mutt_hcache_fetch_raw(header_cache_t *h, const char *filename,
   rv = tcbdbget(h->db, mutt_b2s(path), ksize, &sp);
 #elif HAVE_KC
   rv = kcdbget(h->db, mutt_b2s(path), ksize, &sp);
+#elif HAVE_TKRZW
+  rv = tkrzw_dbm_get(h->db, mutt_b2s(path), ksize, NULL);
 #elif HAVE_GDBM
   key.dptr = path->data;
   key.dsize = ksize;
@@ -940,6 +951,8 @@ mutt_hcache_store_raw(header_cache_t *h, const char *filename, void *data,
   rv = tcbdbput(h->db, mutt_b2s(path), ksize, data, dlen);
 #elif HAVE_KC
   rv = kcdbset(h->db, mutt_b2s(path), ksize, data, dlen);
+#elif HAVE_TKRZW
+  rv = tkrzw_dbm_set(h->db, mutt_b2s(path), ksize, data, dlen, true);
 #elif HAVE_GDBM
   key.dptr = path->data;
   key.dsize = ksize;
@@ -1179,6 +1192,69 @@ mutt_hcache_delete(header_cache_t *h, const char *filename,
   return rc;
 }
 
+#elif HAVE_TKRZW
+static int
+hcache_open_tkrzw(struct header_cache *h, const char *path)
+{
+  int rc = -1;
+
+/* Options are discussed at:
+ * https://dbmx.net/tkrzw/api/classtkrzw_1_1PolyDBM.html
+ * in the OpenAdvanced method call.
+ *
+ * Unfortunately the "compression" option requires specifying a specific library,
+ * and mutt has no way of knowing which libraries tkrzw was built with, so we ignore
+ * OPTHCACHECOMPRESS.
+ */
+  h->db = tkrzw_dbm_open(path, true, "dbm=TreeDBM");
+  if (!h->db)
+  {
+    muttdbg(2, "tkrzw_dbm_open failed for %s: %s (%d)", path,
+            tkrzw_get_last_status_message(), tkrzw_get_last_status_code());
+    goto cleanup;
+  }
+
+  rc = 0;
+
+cleanup:
+  return rc;
+}
+
+void
+mutt_hcache_close(header_cache_t *h)
+{
+  if (!h)
+    return;
+
+  if (!tkrzw_dbm_close(h->db))
+    muttdbg(2, "tkrzw_dbm_close failed for %s: %s (ecode %d)", h->folder,
+            tkrzw_get_last_status_message(), tkrzw_get_last_status_code());
+  FREE(&h->folder);
+  FREE(&h);
+}
+
+int
+mutt_hcache_delete(header_cache_t *h, const char *filename,
+                   size_t (*keylen)(const char *fn))
+{
+  BUFFER *path = NULL;
+  int ksize, rc;
+
+  if (!h)
+    return -1;
+
+  path = mutt_buffer_pool_get();
+  mutt_buffer_strcpy(path, h->folder);
+  mutt_buffer_addstr(path, filename);
+
+  ksize = strlen(h->folder) + keylen(filename);
+
+  rc = tkrzw_dbm_remove(h->db, mutt_b2s(path), ksize);
+
+  mutt_buffer_pool_release(&path);
+  return rc;
+}
+
 #elif HAVE_GDBM
 static int
 hcache_open_gdbm(struct header_cache *h, const char *path)
@@ -1478,6 +1554,8 @@ mutt_hcache_open(const char *path, const char *folder, hcache_namer_t namer)
   hcache_open= hcache_open_tc;
 #elif HAVE_KC
   hcache_open= hcache_open_kc;
+#elif HAVE_TKRZW
+  hcache_open = hcache_open_tkrzw;
 #elif HAVE_GDBM
   hcache_open = hcache_open_gdbm;
 #elif HAVE_DB4
@@ -1603,4 +1681,11 @@ const char *mutt_hcache_backend(void)
   snprintf(backend, sizeof(backend), "kyotocabinet %s", KCVERSION);
   return backend;
 }
+#elif HAVE_TKRZW
+const char *mutt_hcache_backend(void)
+{
+  static char backend[SHORT_STRING];
+  snprintf(backend, sizeof(backend), "tkrzw %s", TKRZW_LIBRARY_VERSION);
+  return backend;
+}
 #endif
-- 
2.55.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.