gpg: New caching functions.

* g10/objcache.c: New.
* g10/objcache.h: New.
* g10/Makefile.am (common_source): Add them.
* g10/gpg.c: Include objcache.h.
(g10_exit): Call objcache_dump_stats.
* g10/getkey.c: Include objcache.h.
(get_primary_uid, release_keyid_list): Remove.
(cache_user_id): Remove.
(finish_lookup): Call the new cache_put_keyblock instead of
cache_user_id.
(get_user_id_string): Remove code for mode 2.
(get_user_id): Implement using cache_get_uid_bykid.
--

This generic caching module is better than the ad-hoc code we used in
getkey.c.  More cleanup in getkey is still required but it is a
start.  There is also a small performance increase with the new cache:

With a large keyring and --list-sigs I get these numbers:

|      | before     | after      |
|------+------------+------------|
| real | 14m1.028s  | 12m16.186s |
| user |  2m18.484s |  1m36.040s |
| sys  | 11m42.420s | 10m40.044s |

Note the speedup in the user time which is due to the improved cache
algorithm.  This is obvious, because the old cache was just a long
linked list; the new cache are two hash tables.

Signed-off-by: Werner Koch <wk@gnupg.org>
This commit is contained in:
Werner Koch 2019-04-13 11:48:58 +02:00
parent 60f3845921
commit 64a5fd3727
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
5 changed files with 702 additions and 119 deletions

View File

@ -121,6 +121,7 @@ common_source = \
sig-check.c \
keylist.c \
pkglue.c pkglue.h \
objcache.c objcache.h \
ecdh.c
gpg_sources = server.c \

View File

@ -36,6 +36,7 @@
#include "../common/i18n.h"
#include "keyserver-internal.h"
#include "call-agent.h"
#include "objcache.h"
#include "../common/host2net.h"
#include "../common/mbox-util.h"
#include "../common/status.h"
@ -141,7 +142,6 @@ typedef struct user_id_db
char name[1];
} *user_id_db_t;
static user_id_db_t user_id_db;
static int uid_cache_entries; /* Number of entries in uid cache. */
static void merge_selfsigs (ctrl_t ctrl, kbnode_t keyblock);
static int lookup (ctrl_t ctrl, getkey_ctx_t ctx, int want_secret,
@ -263,115 +263,6 @@ user_id_not_found_utf8 (void)
/* Return the user ID from the given keyblock.
* We use the primary uid flag which has been set by the merge_selfsigs
* function. The returned value is only valid as long as the given
* keyblock is not changed. */
static const char *
get_primary_uid (KBNODE keyblock, size_t * uidlen)
{
KBNODE k;
const char *s;
for (k = keyblock; k; k = k->next)
{
if (k->pkt->pkttype == PKT_USER_ID
&& !k->pkt->pkt.user_id->attrib_data
&& k->pkt->pkt.user_id->flags.primary)
{
*uidlen = k->pkt->pkt.user_id->len;
return k->pkt->pkt.user_id->name;
}
}
s = user_id_not_found_utf8 ();
*uidlen = strlen (s);
return s;
}
static void
release_keyid_list (keyid_list_t k)
{
while (k)
{
keyid_list_t k2 = k->next;
xfree (k);
k = k2;
}
}
/****************
* Store the association of keyid and userid
* Feed only public keys to this function.
*/
static void
cache_user_id (KBNODE keyblock)
{
user_id_db_t r;
const char *uid;
size_t uidlen;
keyid_list_t keyids = NULL;
KBNODE k;
size_t n;
for (k = keyblock; k; k = k->next)
{
if (k->pkt->pkttype == PKT_PUBLIC_KEY
|| k->pkt->pkttype == PKT_PUBLIC_SUBKEY)
{
keyid_list_t a = xmalloc_clear (sizeof *a);
/* Hmmm: For a long list of keyids it might be an advantage
* to append the keys. */
fingerprint_from_pk (k->pkt->pkt.public_key, a->fpr, &n);
a->fprlen = n;
keyid_from_pk (k->pkt->pkt.public_key, a->keyid);
/* First check for duplicates. */
for (r = user_id_db; r; r = r->next)
{
keyid_list_t b;
for (b = r->keyids; b; b = b->next)
{
if (b->fprlen == a->fprlen
&& !memcmp (b->fpr, a->fpr, a->fprlen))
{
if (DBG_CACHE)
log_debug ("cache_user_id: already in cache\n");
release_keyid_list (keyids);
xfree (a);
return;
}
}
}
/* Now put it into the cache. */
a->next = keyids;
keyids = a;
}
}
if (!keyids)
BUG (); /* No key no fun. */
uid = get_primary_uid (keyblock, &uidlen);
if (uid_cache_entries >= MAX_UID_CACHE_ENTRIES)
{
/* fixme: use another algorithm to free some cache slots */
r = user_id_db;
user_id_db = r->next;
release_keyid_list (r->keyids);
xfree (r);
uid_cache_entries--;
}
r = xmalloc (sizeof *r + uidlen - 1);
r->keyids = keyids;
r->len = uidlen;
memcpy (r->name, uid, r->len);
r->next = user_id_db;
user_id_db = r;
uid_cache_entries++;
}
/* Disable and drop the public key cache (which is filled by
cache_public_key and get_pubkey). Note: there is currently no way
@ -3627,7 +3518,7 @@ finish_lookup (kbnode_t keyblock, unsigned int req_usage, int want_exact,
xfree (tempkeystr);
}
cache_user_id (keyblock);
cache_put_keyblock (keyblock);
return latest_key ? latest_key : keyblock; /* Found. */
}
@ -3848,6 +3739,7 @@ get_user_id_string (ctrl_t ctrl, u32 * keyid, int mode, size_t *r_len,
int pass = 0;
char *p;
log_assert (mode != 2);
if (r_nouid)
*r_nouid = 0;
@ -3862,13 +3754,7 @@ get_user_id_string (ctrl_t ctrl, u32 * keyid, int mode, size_t *r_len,
{
if (mode == 2)
{
/* An empty string as user id is possible. Make
sure that the malloc allocates one byte and
does not bail out. */
p = xmalloc (r->len? r->len : 1);
memcpy (p, r->name, r->len);
if (r_len)
*r_len = r->len;
BUG ();
}
else
{
@ -3926,7 +3812,31 @@ get_long_user_id_string (ctrl_t ctrl, u32 * keyid)
char *
get_user_id (ctrl_t ctrl, u32 *keyid, size_t *rn, int *r_nouid)
{
return get_user_id_string (ctrl, keyid, 2, rn, r_nouid);
char *name;
unsigned int namelen;
if (r_nouid)
*r_nouid = 0;
name = cache_get_uid_bykid (keyid, &namelen);
if (!name)
{
/* Get it so that the cache will be filled. */
if (!get_pubkey (ctrl, NULL, keyid))
name = cache_get_uid_bykid (keyid, &namelen);
}
if (!name)
{
name = xstrdup (user_id_not_found_utf8 ());
namelen = strlen (name);
if (r_nouid)
*r_nouid = 1;
}
if (rn && name)
*rn = namelen;
return name;
}

View File

@ -59,6 +59,7 @@
#include "../common/asshelp.h"
#include "call-dirmngr.h"
#include "tofu.h"
#include "objcache.h"
#include "../common/init.h"
#include "../common/mbox-util.h"
#include "../common/shareddefs.h"
@ -5223,6 +5224,7 @@ g10_exit( int rc )
{
keydb_dump_stats ();
sig_check_dump_stats ();
objcache_dump_stats ();
gcry_control (GCRYCTL_DUMP_MEMORY_STATS);
gcry_control (GCRYCTL_DUMP_RANDOM_STATS);
}

642
g10/objcache.c Normal file
View File

@ -0,0 +1,642 @@
/* objcache.c - Caching functions for keys and user ids.
* Copyright (C) 2019 g10 Code GmbH
*
* This file is part of GnuPG.
*
* GnuPG is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GnuPG is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <https://www.gnu.org/licenses/>.
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "gpg.h"
#include "../common/util.h"
#include "packet.h"
#include "keydb.h"
#include "options.h"
#include "objcache.h"
/* Note that max value for uid_items is actually a the threshold when
* we start to look for ietms which can be removed. */
#define NO_OF_UID_ITEM_BUCKETS 107
#define MAX_UID_ITEMS_PER_BUCKET 20
#define NO_OF_KEY_ITEM_BUCKETS 383
#define MAX_KEY_ITEMS_PER_BUCKET 20
/* An object to store a user id. This describes an item in the linked
* lists of a bucket in hash table. The reference count will
* eventually be used to remove items from the table. */
typedef struct uid_item_s
{
struct uid_item_s *next;
unsigned int refcount; /* The reference count for this item. */
unsigned int namelen; /* The length of the UID sans the nul. */
char name[1];
} *uid_item_t;
static uid_item_t *uid_table; /* Hash table for with user ids. */
static size_t uid_table_size; /* Number of allocated buckets. */
static unsigned int uid_table_max; /* Max. # of items in a bucket. */
static unsigned int uid_table_added; /* # of items added. */
static unsigned int uid_table_dropped;/* # of items dropped. */
/* An object to store properties of a key. Note that this can be used
* for a primary or a subkey. The key is linked to a user if that
* exists. */
typedef struct key_item_s
{
struct key_item_s *next;
unsigned int usecount;
byte fprlen;
char fpr[MAX_FINGERPRINT_LEN];
u32 keyid[2];
uid_item_t ui; /* NULL of a ref'ed user id item. */
} *key_item_t;
static key_item_t *key_table; /* Hash table with the keys. */
static size_t key_table_size; /* Number of allocated buckents. */
static unsigned int key_table_max; /* Max. # of items in a bucket. */
static unsigned int key_table_added; /* # of items added. */
static unsigned int key_table_dropped;/* # of items dropped. */
static key_item_t key_item_attic; /* List of freed items. */
/* Dump stats. */
void
objcache_dump_stats (void)
{
unsigned int idx;
int len, minlen, maxlen;
unsigned int count, attic, empty;
key_item_t ki;
uid_item_t ui;
count = empty = 0;
minlen = -1;
maxlen = 0;
for (idx = 0; idx < key_table_size; idx++)
{
len = 0;
for (ki = key_table[idx]; ki; ki = ki->next)
{
count++;
len++;
/* log_debug ("key bucket %u: kid=%08lX used=%u ui=%p\n", */
/* idx, (ulong)ki->keyid[0], ki->usecount, ki->ui); */
}
if (len > maxlen)
maxlen = len;
if (!len)
empty++;
else if (minlen == -1 || len < minlen)
minlen = len;
}
for (attic=0, ki = key_item_attic; ki; ki = ki->next)
attic++;
log_info ("objcache: keys=%u/%u/%u chains=%u,%d..%d buckets=%zu/%u"
" attic=%u\n",
count, key_table_added, key_table_dropped,
empty, minlen > 0? minlen : 0, maxlen,
key_table_size, key_table_max, attic);
count = empty = 0;
minlen = -1;
maxlen = 0;
for (idx = 0; idx < uid_table_size; idx++)
{
len = 0;
for (ui = uid_table[idx]; ui; ui = ui->next)
{
count++;
len++;
/* log_debug ("uid bucket %u: %p ref=%u l=%u (%.20s)\n", */
/* idx, ui, ui->refcount, ui->namelen, ui->name); */
}
if (len > maxlen)
maxlen = len;
if (!len)
empty++;
else if (minlen == -1 || len < minlen)
minlen = len;
}
log_info ("objcache: uids=%u/%u/%u chains=%u,%d..%d buckets=%zu/%u\n",
count, uid_table_added, uid_table_dropped,
empty, minlen > 0? minlen : 0, maxlen,
uid_table_size, uid_table_max);
}
/* The hash function we use for the uid_table. Must not call a system
* function. */
static inline unsigned int
uid_table_hasher (const char *name, unsigned namelen)
{
const unsigned char *s = (const unsigned char*)name;
unsigned int hashval = 0;
unsigned int carry;
for (; namelen; namelen--, s++)
{
hashval = (hashval << 4) + *s;
if ((carry = (hashval & 0xf0000000)))
{
hashval ^= (carry >> 24);
hashval ^= carry;
}
}
return hashval % uid_table_size;
}
/* Run time allocation of the uid table. This allows us to eventually
* add an option to gpg to control the size. */
static void
uid_table_init (void)
{
if (uid_table)
return;
uid_table_size = NO_OF_UID_ITEM_BUCKETS;
uid_table_max = MAX_UID_ITEMS_PER_BUCKET;
uid_table = xcalloc (uid_table_size, sizeof *uid_table);
}
static uid_item_t
uid_item_ref (uid_item_t ui)
{
if (ui)
ui->refcount++;
return ui;
}
static void
uid_item_unref (uid_item_t uid)
{
if (!uid)
return;
if (!uid->refcount)
log_fatal ("too many unrefs for uid_item\n");
uid->refcount--;
/* We do not release the item here because that would require that
* we locate the head of the list which has this item. This will
* take too long and thus the item is removed when we need to purge
* some items for the list during uid_item_put. */
}
/* Put (NAME,NAMELEN) into the UID_TABLE and return the item. The
* reference count for that item is incremented. NULL is return on an
* allocation error. The caller should release the returned item
* using uid_item_unref. */
static uid_item_t
uid_table_put (const char *name, unsigned int namelen)
{
unsigned int hash;
uid_item_t ui;
unsigned int count;
if (!uid_table)
uid_table_init ();
hash = uid_table_hasher (name, namelen);
for (ui = uid_table[hash], count = 0; ui; ui = ui->next, count++)
if (ui->namelen == namelen && !memcmp (ui->name, name, namelen))
return uid_item_ref (ui); /* Found. */
/* If the bucket is full remove all unrefed items. */
if (count >= uid_table_max)
{
uid_item_t ui_next, ui_prev, list_head, drop_head;
/* No syscalls from here .. */
list_head = uid_table[hash];
drop_head = NULL;
while (list_head && !list_head->refcount)
{
ui = list_head;
list_head = ui->next;
ui->next = drop_head;
drop_head = ui;
}
if ((ui_prev = list_head))
for (ui = ui_prev->next; ui; ui = ui_next)
{
ui_next = ui->next;
if (!ui->refcount)
{
ui->next = drop_head;
drop_head = ui;
ui_prev->next = ui_next;
}
else
ui_prev = ui;
}
uid_table[hash] = list_head;
/* ... to here */
for (ui = drop_head; ui; ui = ui_next)
{
ui_next = ui->next;
xfree (ui);
uid_table_dropped++;
}
}
count = uid_table_added + uid_table_dropped;
ui = xtrycalloc (1, sizeof *ui + namelen);
if (!ui)
return NULL; /* Out of core. */
if (count != uid_table_added + uid_table_dropped)
{
/* During the malloc another thread added an item. Thus we need
* to check again. */
uid_item_t ui_new = ui;
for (ui = uid_table[hash]; ui; ui = ui->next)
if (ui->namelen == namelen && !memcmp (ui->name, name, namelen))
{
/* Found. */
xfree (ui_new);
return uid_item_ref (ui);
}
ui = ui_new;
}
memcpy (ui->name, name, namelen);
ui->name[namelen] = 0; /* Extra Nul so we can use it as a string. */
ui->namelen = namelen;
ui->refcount = 1;
ui->next = uid_table[hash];
uid_table[hash] = ui;
uid_table_added++;
return ui;
}
/* The hash function we use for the key_table. Must not call a system
* function. */
static inline unsigned int
key_table_hasher (u32 *keyid)
{
/* A fingerprint could be used directly as a hash value. However,
* we use the keyid here because it is used in encrypted packets and
* older signatures to identify a key. Since v4 keys the keyid is
* anyway a part of the fingerprint so it quickly extracted from a
* fingerprint. Note that v3 keys are not supported by gpg. */
return keyid[0] % key_table_size;
}
/* Run time allocation of the key table. This allows us to eventually
* add an option to gpg to control the size. */
static void
key_table_init (void)
{
if (key_table)
return;
key_table_size = NO_OF_KEY_ITEM_BUCKETS;
key_table_max = MAX_KEY_ITEMS_PER_BUCKET;
key_table = xcalloc (key_table_size, sizeof *key_table);
}
static void
key_item_free (key_item_t ki)
{
if (!ki)
return;
uid_item_unref (ki->ui);
ki->ui = NULL;
ki->next = key_item_attic;
key_item_attic = ki;
}
/* Get a key item from PK or if that is NULL from KEYID. The
* reference count for that item is incremented. NULL is return if it
* was not found. */
static key_item_t
key_table_get (PKT_public_key *pk, u32 *keyid)
{
unsigned int hash;
key_item_t ki, ki2;
if (!key_table)
key_table_init ();
if (pk)
{
byte fpr[MAX_FINGERPRINT_LEN];
size_t fprlen;
u32 tmpkeyid[2];
fingerprint_from_pk (pk, fpr, &fprlen);
keyid_from_pk (pk, tmpkeyid);
hash = key_table_hasher (tmpkeyid);
for (ki = key_table[hash]; ki; ki = ki->next)
if (ki->fprlen == fprlen && !memcmp (ki->fpr, fpr, fprlen))
return ki; /* Found */
}
else if (keyid)
{
hash = key_table_hasher (keyid);
for (ki = key_table[hash]; ki; ki = ki->next)
if (ki->keyid[0] == keyid[0] && ki->keyid[1] == keyid[1])
{
/* Found. We need to check for dups. */
for (ki2 = ki->next; ki2; ki2 = ki2->next)
if (ki2->keyid[0] == keyid[0] && ki2->keyid[1] == keyid[1])
return NULL; /* Duplicated keyid - retrun NULL. */
/* This is the only one - return it. */
return ki;
}
}
return NULL;
}
/* Helper for the qsort in key_table_put. */
static int
compare_key_items (const void *arg_a, const void *arg_b)
{
const key_item_t a = *(const key_item_t *)arg_a;
const key_item_t b = *(const key_item_t *)arg_b;
/* Reverse sort on the usecount. */
if (a->usecount > b->usecount)
return -1;
else if (a->usecount == b->usecount)
return 0;
else
return 1;
}
/* Put PK into the KEY_TABLE and return a key item. The reference
* count for that item is incremented. If UI is given it is put into
* the entry. NULL is return on an allocation error. */
static key_item_t
key_table_put (PKT_public_key *pk, uid_item_t ui)
{
unsigned int hash;
key_item_t ki;
u32 keyid[2];
byte fpr[MAX_FINGERPRINT_LEN];
size_t fprlen;
unsigned int count, n;
if (!key_table)
key_table_init ();
fingerprint_from_pk (pk, fpr, &fprlen);
keyid_from_pk (pk, keyid);
hash = key_table_hasher (keyid);
for (ki = key_table[hash], count=0; ki; ki = ki->next, count++)
if (ki->fprlen == fprlen && !memcmp (ki->fpr, fpr, fprlen))
return ki; /* Found */
/* If the bucket is full remove a couple of items. */
if (count >= key_table_max)
{
key_item_t list_head, *list_tailp, ki_next;
key_item_t *array;
int narray, idx;
/* Unlink from the global list so that other threads don't
* disturb us. If another thread adds or removes something only
* one will be the winner. Bad luck for the drooped cache items
* but after all it is just a cache. */
list_head = key_table[hash];
key_table[hash] = NULL;
/* Put all items into an array for sorting. */
array = xtrycalloc (count, sizeof *array);
if (!array)
{
/* That's bad; give up all items of the bucket. */
log_info ("Note: malloc failed while purging from the key_tabe: %s\n",
gpg_strerror (gpg_error_from_syserror ()));
goto leave_drop;
}
narray = 0;
for (ki = list_head; ki; ki = ki_next)
{
ki_next = ki->next;
array[narray++] = ki;
ki->next = NULL;
}
log_assert (narray == count);
/* Sort the array and put half of it onto a new list. */
qsort (array, narray, sizeof *array, compare_key_items);
list_head = NULL;
list_tailp = &list_head;
for (idx=0; idx < narray/2; idx++)
{
*list_tailp = array[idx];
list_tailp = &array[idx]->next;
}
/* Put the new list into the bucket. */
ki = key_table[hash];
key_table[hash] = list_head;
list_head = ki;
/* Free the remaining items and the array. */
for (; idx < narray; idx++)
{
key_item_free (array[idx]);
key_table_dropped++;
}
xfree (array);
leave_drop:
/* Free any items added in the meantime by other threads. This
* is also used in case of a malloc problem (which won't update
* the counters, though). */
for ( ; list_head; list_head = ki_next)
{
ki_next = list_head->next;
key_item_free (list_head);
}
}
/* Add an item to the bucket. We allocate a whole block of items
* for cache performace reasons. */
if (!key_item_attic)
{
key_item_t kiblock;
int kiblocksize = 256;
kiblock = xtrymalloc (kiblocksize * sizeof *kiblock);
if (!kiblock)
return NULL; /* Out of core. */
for (n = 0; n < kiblocksize; n++)
{
ki = kiblock + n;
ki->next = key_item_attic;
key_item_attic = ki;
}
/* During the malloc another thread may have changed the bucket.
* Thus we need to check again. */
for (ki = key_table[hash]; ki; ki = ki->next)
if (ki->fprlen == fprlen && !memcmp (ki->fpr, fpr, fprlen))
return ki; /* Found */
}
/* We now know that there is an item in the attic. */
ki = key_item_attic;
key_item_attic = ki->next;
ki->next = NULL;
memcpy (ki->fpr, fpr, fprlen);
ki->fprlen = fprlen;
ki->keyid[0] = keyid[0];
ki->keyid[1] = keyid[1];
ki->ui = uid_item_ref (ui);
ki->usecount = 0;
ki->next = key_table[hash];
key_table[hash] = ki;
key_table_added++;
return ki;
}
/* Return the user ID from the given keyblock. We use the primary uid
* flag which should have already been set. The returned value is
* only valid as long as the given keyblock is not changed. */
static const char *
primary_uid_from_keyblock (kbnode_t keyblock, size_t *uidlen)
{
kbnode_t k;
for (k = keyblock; k; k = k->next)
{
if (k->pkt->pkttype == PKT_USER_ID
&& !k->pkt->pkt.user_id->attrib_data
&& k->pkt->pkt.user_id->flags.primary)
{
*uidlen = k->pkt->pkt.user_id->len;
return k->pkt->pkt.user_id->name;
}
}
return NULL;
}
/* Store the associations of keyid/fingerprint and userid. Only
* public keys should be fed to this function. */
void
cache_put_keyblock (kbnode_t keyblock)
{
uid_item_t ui = NULL;
kbnode_t k;
restart:
for (k = keyblock; k; k = k->next)
{
if (k->pkt->pkttype == PKT_PUBLIC_KEY
|| k->pkt->pkttype == PKT_PUBLIC_SUBKEY)
{
if (!ui)
{
/* Initially we just test for an entry to avoid the need
* to create a user id item for a put. Only if we miss
* key in the cache we create a user id and restart. */
if (!key_table_get (k->pkt->pkt.public_key, NULL))
{
const char *uid;
size_t uidlen;
uid = primary_uid_from_keyblock (keyblock, &uidlen);
if (uid)
{
ui = uid_table_put (uid, uidlen);
if (!ui)
{
log_info ("Note: failed to cache a user id: %s\n",
gpg_strerror (gpg_error_from_syserror ()));
goto leave;
}
goto restart;
}
}
}
else /* With a UID we use the update cache mode. */
{
if (!key_table_put (k->pkt->pkt.public_key, ui))
{
log_info ("Note: failed to cache a key: %s\n",
gpg_strerror (gpg_error_from_syserror ()));
goto leave;
}
}
}
}
leave:
uid_item_unref (ui);
}
/* Return the user id string for KEYID. If a user id is not found (or
* on malloc error) NULL is returned. If R_LENGTH is not NULL the
* length of the user id is stored there; this does not included the
* always appended nul. Note that a user id may include an internal
* nul which can be detected by the caller by comparing to the
* returned length. */
char *
cache_get_uid_bykid (u32 *keyid, unsigned int *r_length)
{
key_item_t ki;
char *p;
if (r_length)
*r_length = 0;
ki = key_table_get (NULL, keyid);
if (!ki)
return NULL; /* Not found or duplicate keyid. */
if (!ki->ui)
p = NULL; /* No user id known for key. */
else
{
p = xtrymalloc (ki->ui->namelen + 1);
if (p)
{
memcpy (p, ki->ui->name, ki->ui->namelen + 1);
if (r_length)
*r_length = ki->ui->namelen;
ki->usecount++;
}
}
return p;
}

28
g10/objcache.h Normal file
View File

@ -0,0 +1,28 @@
/* objcache.h - Caching functions for keys and user ids.
* Copyright (C) 2019 g10 Code GmbH
*
* This file is part of GnuPG.
*
* GnuPG is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GnuPG is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <https://www.gnu.org/licenses/>.
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#ifndef GNUPG_G10_OBJCACHE_H
#define GNUPG_G10_OBJCACHE_H
void objcache_dump_stats (void);
void cache_put_keyblock (kbnode_t keyblock);
char *cache_get_uid_bykid (u32 *keyid, unsigned int *r_length);
#endif /*GNUPG_G10_OBJCACHE_H*/