summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthias Hopf <mhopf@suse.de>2006-06-13 20:23:46 +0200
committerDonnie Berkholz <spyderous@gentoo.org>2006-06-22 15:27:26 -0700
commit13968a23aaea838ba4b69e42e8900f803499e091 (patch)
treee941ecaa51cdd7581c8ce47a3035a7f94b1f9c8b
parente7f8bca08f5476d0ca262097639ac7d424bb4d10 (diff)
downloadxorg-lib-libX11-13968a23aaea838ba4b69e42e8900f803499e091.tar.gz
Bug #3104: Compose table cache for faster X11 application starts.
(cherry picked from 4fe22647e6010a2886c2f3a7093adeaeb6678faa commit)
-rw-r--r--configure.ac10
-rw-r--r--modules/im/ximcp/imDefIm.c2
-rw-r--r--modules/im/ximcp/imLcIm.c388
-rw-r--r--src/xlibi18n/Ximint.h4
4 files changed, 386 insertions, 18 deletions
diff --git a/configure.ac b/configure.ac
index 6f4a19e5..4154ebc2 100644
--- a/configure.ac
+++ b/configure.ac
@@ -288,6 +288,15 @@ else
fi
AC_SUBST(XKBPROTO_REQUIRES)
+AC_FUNC_MMAP()
+AC_ARG_ENABLE(composecache,
+ AC_HELP_STRING([--disable-composecache],
+ [Disable compose table cache support]),
+ [COMPOSECACHE=$enableval],[COMPOSECACHE=$ac_cv_func_mmap_fixed_mapped])
+if test x"$COMPOSECACHE" = "xyes"; then
+ AC_DEFINE(COMPOSECACHE,1,[Include compose table cache support])
+fi
+
if test "x$GCC" = "xyes"; then
GCC_WARNINGS="-Wall -Wpointer-arith -Wstrict-prototypes \
-Wmissing-prototypes -Wmissing-declarations \
@@ -420,4 +429,5 @@ echo " XF86BigFont support: "$XF86BIGFONT
echo " XKB support: "$XKB
echo " XLOCALEDIR environment variable support: "$ENABLE_XLOCALEDIR
echo " Manual pages installed: "$LIBMAN_SUFFIX
+echo " Compose table cache enabled: "$COMPOSECACHE
echo ""
diff --git a/modules/im/ximcp/imDefIm.c b/modules/im/ximcp/imDefIm.c
index 7581548e..a1c83a91 100644
--- a/modules/im/ximcp/imDefIm.c
+++ b/modules/im/ximcp/imDefIm.c
@@ -76,7 +76,7 @@ _XimSetHeader(
return;
}
-Private char
+Public char
_XimGetMyEndian(void)
{
CARD16 test_card = 1;
diff --git a/modules/im/ximcp/imLcIm.c b/modules/im/ximcp/imLcIm.c
index 37271ae4..ce01cc45 100644
--- a/modules/im/ximcp/imLcIm.c
+++ b/modules/im/ximcp/imLcIm.c
@@ -50,6 +50,49 @@ THIS SOFTWARE.
#include "XlcPubI.h"
#include "Ximint.h"
#include <ctype.h>
+#include <assert.h>
+
+#ifdef COMPOSECACHE
+# include <sys/types.h>
+# include <sys/stat.h>
+# include <sys/mman.h>
+#endif
+
+
+#ifdef COMPOSECACHE
+
+/* include trailing '/' for cache directory, file prefix otherwise */
+#define XIM_GLOBAL_CACHE_DIR "/var/X11R6/compose-cache/"
+#define XIM_HOME_CACHE_DIR "/.compose-cache/"
+#define XIM_CACHE_MAGIC ('X' | 'i'<<8 | 'm'<<16 | 'C'<<24)
+#define XIM_CACHE_VERSION 2
+
+#define XIM_HASH_PRIME_1 13
+#define XIM_HASH_PRIME_2 1234096939
+
+typedef INT32 DTStructIndex;
+struct _XimCacheStruct {
+ INT32 id;
+ INT32 version;
+ DTStructIndex tree;
+ DTStructIndex mb;
+ DTStructIndex wc;
+ DTStructIndex utf8;
+ DTStructIndex size;
+ DTIndex top;
+ DTIndex treeused;
+ DTCharIndex mbused;
+ DTCharIndex wcused;
+ DTCharIndex utf8used;
+ char fname[1];
+};
+
+Private struct _XimCacheStruct* _XimCache_mmap = NULL;
+Private DefTreeBase _XimCachedDefaultTreeBase;
+Private int _XimCachedDefaultTreeRefcount = 0;
+
+#endif
+
Public Bool
_XimCheckIfLocalProcessing(im)
@@ -81,6 +124,14 @@ XimFreeDefaultTree(
DefTreeBase *b)
{
if (!b) return;
+ if (b->tree == NULL) return;
+#ifdef COMPOSECACHE
+ if (b->tree == _XimCachedDefaultTreeBase.tree) {
+ _XimCachedDefaultTreeRefcount--;
+ /* No deleting, it's a cache after all. */
+ return;
+ }
+#endif
if (b->tree) Xfree (b->tree);
if (b->mb) Xfree (b->mb);
if (b->wc) Xfree (b->wc);
@@ -219,28 +270,255 @@ _XimLocalSetIMValues(
return(name);
}
+
+#ifdef COMPOSECACHE
+
+Private Bool
+_XimReadCachedDefaultTree(
+ int fd_cache,
+ const char *name,
+ DTStructIndex size)
+{
+ struct _XimCacheStruct* m;
+ m = mmap (NULL, size, PROT_READ, MAP_PRIVATE, fd_cache, 0);
+ if (m == NULL || m == MAP_FAILED)
+ return False;
+ assert (m->id == XIM_CACHE_MAGIC);
+ assert (m->version == XIM_CACHE_VERSION);
+ if (size != m->size ||
+ size <= XOffsetOf (struct _XimCacheStruct, fname) + strlen (name)) {
+ fprintf (stderr, "Ignoring broken XimCache %s\n", name);
+ munmap (m, size);
+ return False;
+ }
+ if (strncmp (name, m->fname, strlen (name)+1) != 0) {
+ /* m->defs[0].mb may *not* be terminated - but who cares here */
+ fprintf (stderr, "Hash clash - expected %s, got %s\n",
+ name, m->fname);
+ munmap (m, size);
+ return False;
+ }
+ _XimCache_mmap = m;
+ _XimCachedDefaultTreeBase.tree = (DefTree *) (((char *) m) + m->tree);
+ _XimCachedDefaultTreeBase.mb = (((char *) m) + m->mb);
+ _XimCachedDefaultTreeBase.wc = (wchar_t *) (((char *) m) + m->wc);
+ _XimCachedDefaultTreeBase.utf8 = (((char *) m) + m->utf8);
+ _XimCachedDefaultTreeBase.treeused = m->treeused;
+ _XimCachedDefaultTreeBase.mbused = m->mbused;
+ _XimCachedDefaultTreeBase.wcused = m->wcused;
+ _XimCachedDefaultTreeBase.utf8used = m->utf8used;
+ /* treesize etc. is ignored because only used during parsing */
+ _XimCachedDefaultTreeRefcount = 0;
+/* fprintf (stderr, "read cached tree at %p: %s\n", (void *) m, name); */
+ return True;
+}
+
+Private unsigned int strToHash (
+ const char *name)
+{
+ unsigned int hash = 0;
+ while (*name)
+ hash = hash * XIM_HASH_PRIME_1 + *(unsigned const char *)name++;
+ return hash % XIM_HASH_PRIME_2;
+}
+
+
+/* Returns read-only fd of cache file, -1 if none.
+ * Sets *res to cache filename if safe. Sets *size to file size of cache. */
+Private int _XimCachedFileName (
+ const char *dir, const char *name, const char *intname,
+ uid_t uid, int isglobal, char **res, off_t *size)
+{
+ struct stat st_name, st;
+ int fd;
+ unsigned int len, hash;
+ struct _XimCacheStruct* m;
+ /* There are some races here with 'dir', but we are either in our own home
+ * or the global cache dir, and not inside some public writable dir */
+/* fprintf (stderr, "XimCachedFileName for dir %s name %s intname %s uid %d\n", dir, name, intname, uid); */
+ if (stat (name, &st_name) == -1 || ! S_ISREG (st_name.st_mode)
+ || stat (dir, &st) == -1 || ! S_ISDIR (st.st_mode) || st.st_uid != uid
+ || (st.st_mode & 0022) != 0000) {
+ *res = NULL;
+ return -1;
+ }
+ len = strlen (dir);
+ hash = strToHash (intname);
+ *res = Xmalloc (len + 1 + 18 + 1); /* Max VERSION 9999 */
+
+ if (len == 0 || dir [len-1] != '/')
+ sprintf (*res, "%s/%c%d_%03x_%08x", dir, _XimGetMyEndian(),
+ XIM_CACHE_VERSION, sizeof (DefTree), hash);
+ else
+ sprintf (*res, "%s%c%d_%03x_%08x", dir, _XimGetMyEndian(),
+ XIM_CACHE_VERSION, sizeof (DefTree), hash);
+
+/* fprintf (stderr, "-> %s\n", *res); */
+ if ( (fd = _XOpenFile (*res, O_RDONLY)) == -1)
+ return -1;
+
+ if (fstat (fd, &st) == -1) {
+ Xfree (*res);
+ *res = NULL;
+ close (fd);
+ return -1;
+ }
+ *size = st.st_size;
+
+ if (! S_ISREG (st.st_mode) || st.st_uid != uid
+ || (st.st_mode & 0022) != 0000 || st.st_mtime <= st_name.st_mtime
+ || (st.st_mtime < time (NULL) - 24*60*60 && ! isglobal)) {
+
+ close (fd);
+ if (unlink (*res) != 0) {
+ Xfree (*res);
+ *res = NULL; /* cache is not safe */
+ }
+ return -1;
+ }
+
+ m = mmap (NULL, sizeof (struct _XimCacheStruct), PROT_READ, MAP_PRIVATE,
+ fd, 0);
+ if (m == NULL || m == MAP_FAILED) {
+ close (fd);
+ Xfree (*res);
+ *res = NULL;
+ return -1;
+ }
+ if (*size < sizeof (struct _XimCacheStruct) || m->id != XIM_CACHE_MAGIC) {
+ munmap (m, sizeof (struct _XimCacheStruct));
+ close (fd);
+ fprintf (stderr, "Ignoring broken XimCache %s\n", *res);
+ Xfree (*res);
+ *res = NULL;
+ return -1;
+ }
+ if (m->version != XIM_CACHE_VERSION) {
+ munmap (m, sizeof (struct _XimCacheStruct));
+ close (fd);
+ if (unlink (*res) != 0) {
+ Xfree (*res);
+ *res = NULL; /* cache is not safe */
+ }
+ return -1;
+ }
+ munmap (m, sizeof (struct _XimCacheStruct));
+
+ return fd;
+}
+
+
+Private Bool _XimLoadCache (
+ int fd,
+ const char *name,
+ off_t size,
+ Xim im)
+{
+ if (_XimCache_mmap ||
+ _XimReadCachedDefaultTree (fd, name, size)) {
+ _XimCachedDefaultTreeRefcount++;
+ memcpy (&im->private.local.base, &_XimCachedDefaultTreeBase,
+ sizeof (_XimCachedDefaultTreeBase));
+ im->private.local.top = _XimCache_mmap->top;
+ return True;
+ }
+
+ return False;
+}
+
+
+Private void
+_XimWriteCachedDefaultTree(
+ const char *name,
+ const char *cachename,
+ Xim im)
+{
+ int fd;
+ FILE *fp;
+ struct _XimCacheStruct *m;
+ int msize = XOffsetOf(struct _XimCacheStruct, fname) + strlen(name) + 1;
+ DefTreeBase *b = &im->private.local.base;
+
+ if (! b->tree && ! (b->tree = Xmalloc (sizeof(DefTree))) )
+ return;
+ if (! b->mb && ! (b->mb = Xmalloc (1)) )
+ return;
+ if (! b->wc && ! (b->wc = Xmalloc (sizeof(wchar_t))) )
+ return;
+ if (! b->utf8 && ! (b->utf8 = Xmalloc (1)) )
+ return;
+
+ m = Xmalloc (msize);
+ m->id = XIM_CACHE_MAGIC;
+ m->version = XIM_CACHE_VERSION;
+ m->tree = msize;
+ m->top = im->private.local.top;
+ m->treeused = b->treeused;
+ m->mbused = b->mbused;
+ m->wcused = b->wcused;
+ m->utf8used = b->utf8used;
+ m->mb = msize + sizeof (DefTree) * m->treeused;
+ m->wc = m->mb + m->mbused;
+ m->utf8 = m->wc + sizeof (wchar_t) * m->wcused;
+ m->size = m->utf8 + m->utf8used;
+ strcpy (m->fname, name);
+
+ /* Should use getpwent() instead of $HOME (cross-platform?) */
+ /* This STILL might be racy on NFS */
+ if ( (fd = _XOpenFileMode (cachename, O_WRONLY | O_CREAT | O_EXCL,
+ 0600)) < 0)
+ return;
+ if (! (fp = fdopen (fd, "wb")) ) {
+ close (fd);
+ return;
+ }
+ fwrite (m, msize, 1, fp);
+ fwrite (im->private.local.base.tree, sizeof(DefTree), m->treeused, fp);
+ fwrite (im->private.local.base.mb, 1, m->mbused, fp);
+ fwrite (im->private.local.base.wc, sizeof(wchar_t), m->wcused, fp);
+ fwrite (im->private.local.base.utf8, 1, m->utf8used, fp);
+ if (fclose (fp) != 0)
+ unlink (cachename);
+ _XimCache_mmap = m;
+ memcpy (&_XimCachedDefaultTreeBase, &im->private.local.base,
+ sizeof (_XimCachedDefaultTreeBase));
+/* fprintf (stderr, "wrote tree %s size %ld to %s\n", name, m->size, cachename); */
+}
+
+#endif
+
+
Private void
_XimCreateDefaultTree(
Xim im)
{
FILE *fp = NULL;
- char *name, *tmpname = NULL;
+ char *name, *tmpname = NULL, *intname;
+ char *cachename = NULL;
+ char *home = getenv("HOME");
+ char *cachedir = NULL;
+ char *tmpcachedir = NULL;
+ int hl = home ? strlen (home) : 0;
+#ifdef COMPOSECACHE
+ uid_t euid = geteuid ();
+ gid_t egid = getegid ();
+ int cachefd = -1;
+ off_t size;
+#endif
name = getenv("XCOMPOSEFILE");
-
if (name == (char *) NULL) {
- char *home = getenv("HOME");
if (home != (char *) NULL) {
- int hl = strlen(home);
tmpname = name = Xmalloc(hl + 10 + 1);
if (name != (char *) NULL) {
+ int fd;
strcpy(name, home);
strcpy(name + hl, "/.XCompose");
- fp = _XFopenFile (name, "r");
- if (fp == (FILE *) NULL) {
- Xfree(name);
- name = tmpname = NULL;
- }
+ if ( (fd = _XOpenFile (name, O_RDONLY)) < 0) {
+ Xfree (name);
+ name = tmpname = NULL;
+ } else
+ close (fd);
}
}
}
@@ -248,19 +526,95 @@ _XimCreateDefaultTree(
if (name == (char *) NULL) {
tmpname = name = _XlcFileName(im->core.lcd, COMPOSE_FILE);
}
+ intname = name;
+
+#ifdef COMPOSECACHE
+ if (getuid () == euid && getgid () == egid && euid != 0) {
+ char *c;
+ /* Usage: XCOMPOSECACHE=<cachedir>[=<filename>]
+ * cachedir: directory of cache files
+ * filename: internally used name for cache file */
+ cachedir = getenv("XCOMPOSECACHE");
+ if (cachedir && (c = strchr (cachedir, '='))) {
+ tmpcachedir = strdup (cachedir);
+ intname = tmpcachedir + (c-cachedir) + 1;
+ tmpcachedir[c-cachedir] = '\0';
+ cachedir = tmpcachedir;
+ }
+ }
- if (name == (char *) NULL)
- return;
- if (fp == (FILE *) NULL) {
- fp = _XFopenFile (name, "r");
+ if (! cachedir) {
+ cachefd = _XimCachedFileName (XIM_GLOBAL_CACHE_DIR, name, intname,
+ 0, 1, &cachename, &size);
+ if (cachefd != -1) {
+ if (_XimLoadCache (cachefd, intname, size, im)) {
+ if (tmpcachedir)
+ Xfree (tmpcachedir);
+ if (tmpname)
+ Xfree (tmpname);
+ if (cachename)
+ Xfree (cachename);
+ close (cachefd);
+ return;
+ }
+ close (cachefd);
+ }
+ if (cachename)
+ Xfree (cachename);
+ cachename = NULL;
+ }
+
+ if (getuid () == euid && getgid () == egid && euid != 0 && home) {
+
+ if (! cachedir) {
+ tmpcachedir = cachedir = Xmalloc (hl+strlen(XIM_HOME_CACHE_DIR)+1);
+ strcpy (cachedir, home);
+ strcat (cachedir, XIM_HOME_CACHE_DIR);
+ }
+ cachefd = _XimCachedFileName (cachedir, name, intname, euid, 0,
+ &cachename, &size);
+ if (cachefd != -1) {
+ if (_XimLoadCache (cachefd, intname, size, im)) {
+ if (tmpcachedir)
+ Xfree (tmpcachedir);
+ if (tmpname)
+ Xfree (tmpname);
+ if (cachename)
+ Xfree (cachename);
+ close (cachefd);
+ return;
+ }
+ close (cachefd);
+ }
}
- if (tmpname != (char *) NULL) {
- Xfree(tmpname);
+#endif
+
+ fp = _XFopenFile (name, "r");
+ if (! (fp = _XFopenFile (name, "r"))) {
+ if (tmpcachedir)
+ Xfree (tmpcachedir);
+ if (tmpname)
+ Xfree (tmpname);
+ if (cachename)
+ Xfree (cachename);
+ return;
}
- if (fp == (FILE *) NULL)
- return;
_XimParseStringFile(fp, im);
fclose(fp);
+
+#ifdef COMPOSECACHE
+ if (cachename) {
+ assert (euid != 0);
+ _XimWriteCachedDefaultTree (intname, cachename, im);
+ }
+#endif
+
+ if (tmpcachedir)
+ Xfree (tmpcachedir);
+ if (tmpname)
+ Xfree (tmpname);
+ if (cachename)
+ Xfree (cachename);
}
Private XIMMethodsRec Xim_im_local_methods = {
diff --git a/src/xlibi18n/Ximint.h b/src/xlibi18n/Ximint.h
index c0d1fcbc..4aca3c81 100644
--- a/src/xlibi18n/Ximint.h
+++ b/src/xlibi18n/Ximint.h
@@ -672,6 +672,10 @@ extern int _XimLcctstoutf8(
Status *state
);
+extern char _XimGetMyEndian(
+ void
+);
+
extern int _XimCheckDataSize(
XPointer buf,
int len