Imported Upstream version 0.6.9
[platform/upstream/libsolv.git] / src / repodata.c
index 947ec4b..4ba1345 100644 (file)
@@ -9,7 +9,9 @@
  * repodata.c
  *
  * Manage data coming from one repository
- * 
+ *
+ * a repository can contain multiple repodata entries, consisting of
+ * different sets of keys and different sets of solvables
  */
 
 #define _GNU_SOURCE
 #include <stdlib.h>
 #include <unistd.h>
 #include <assert.h>
+#include <regex.h>
 
 #include "repo.h"
 #include "pool.h"
 #include "poolid_private.h"
 #include "util.h"
+#include "hash.h"
+#include "chksum.h"
 
 #include "repopack.h"
-
-extern unsigned int compress_buf (const unsigned char *in, unsigned int in_len,
-                                 unsigned char *out, unsigned int out_len);
-extern unsigned int unchecked_decompress_buf (const unsigned char *in,
-                                             unsigned int in_len,
-                                             unsigned char *out,
-                                             unsigned int out_len);
+#include "repopage.h"
 
 #define REPODATA_BLOCK 255
 
+static unsigned char *data_skip_key(Repodata *data, unsigned char *dp, Repokey *key);
 
 void
-repodata_init(Repodata *data, Repo *repo, int localpool)
+repodata_initdata(Repodata *data, Repo *repo, int localpool)
 {
   memset(data, 0, sizeof (*data));
+  data->repodataid = data - repo->repodata;
   data->repo = repo;
   data->localpool = localpool;
   if (localpool)
     stringpool_init_empty(&data->spool);
-  data->keys = sat_calloc(1, sizeof(Repokey));
+  /* dirpool_init(&data->dirpool);     just zeros out again */
+  data->keys = solv_calloc(1, sizeof(Repokey));
   data->nkeys = 1;
-  data->schemata = sat_calloc(1, sizeof(Id));
-  data->schemadata = sat_calloc(1, sizeof(Id));
+  data->schemata = solv_calloc(1, sizeof(Id));
+  data->schemadata = solv_calloc(1, sizeof(Id));
   data->nschemata = 1;
   data->schemadatalen = 1;
-  data->start = repo->start;
-  data->end = repo->end;
-  data->nextra = repo->nextra;
-  data->extrastart = 0;
-  data->incoreoffset = sat_extend_resize(0, data->end - data->start, sizeof(Id), REPODATA_BLOCK);
-  data->extraoffset = sat_extend_resize(0, repo->nextra, sizeof(Id), REPODATA_BLOCK);
-  data->pagefd = -1;
+  repopagestore_init(&data->store);
 }
 
 void
-repodata_free(Repodata *data)
+repodata_freedata(Repodata *data)
 {
-  sat_free(data->keys);
-  sat_free(data->schemata);
-  sat_free(data->schemadata);
+  int i;
 
-  sat_free(data->spool.strings);
-  sat_free(data->spool.stringspace);
-  sat_free(data->spool.stringhashtbl);
+  solv_free(data->keys);
 
-  sat_free(data->dirpool.dirs);
-  sat_free(data->dirpool.dirtraverse);
+  solv_free(data->schemata);
+  solv_free(data->schemadata);
+  solv_free(data->schematahash);
 
-  sat_free(data->incoredata);
-  sat_free(data->incoreoffset);
-  sat_free(data->extraoffset);
-  sat_free(data->verticaloffset);
+  stringpool_free(&data->spool);
+  dirpool_free(&data->dirpool);
 
-  sat_free(data->blob_store);
-  sat_free(data->pages);
-  sat_free(data->mapped);
+  solv_free(data->mainschemaoffsets);
+  solv_free(data->incoredata);
+  solv_free(data->incoreoffset);
+  solv_free(data->verticaloffset);
 
-  sat_free(data->vincore);
+  repopagestore_free(&data->store);
 
-  sat_free(data->attrs);
-  sat_free(data->extraattrs);
-  sat_free(data->attrdata);
-  sat_free(data->attriddata);
-  
-  sat_free(data->location);
-  sat_free(data->addedfileprovides);
+  solv_free(data->vincore);
 
-  if (data->pagefd != -1)
-    close(data->pagefd);
+  if (data->attrs)
+    for (i = 0; i < data->end - data->start; i++)
+      solv_free(data->attrs[i]);
+  solv_free(data->attrs);
+  if (data->xattrs)
+    for (i = 0; i < data->nxattrs; i++)
+      solv_free(data->xattrs[i]);
+  solv_free(data->xattrs);
+
+  solv_free(data->attrdata);
+  solv_free(data->attriddata);
+  solv_free(data->attrnum64data);
+
+  solv_free(data->dircache);
 }
 
-static unsigned char *
-forward_to_key(Repodata *data, Id keyid, Id schemaid, unsigned char *dp)
+void
+repodata_free(Repodata *data)
 {
-  Id k, *keyp;
-
-  keyp = data->schemadata + data->schemata[schemaid];
-  while ((k = *keyp++) != 0)
+  Repo *repo = data->repo;
+  int i = data - repo->repodata;
+  if (i == 0)
+    return;
+  repodata_freedata(data);
+  if (i < repo->nrepodata - 1)
     {
-      if (k == keyid)
-       return dp;
-      if (data->keys[k].storage == KEY_STORAGE_VERTICAL_OFFSET)
-       {
-         dp = data_skip(dp, REPOKEY_TYPE_ID);  /* skip that offset */
-         dp = data_skip(dp, REPOKEY_TYPE_ID);  /* skip that length */
-         continue;
-       }
-      if (data->keys[k].storage != KEY_STORAGE_INCORE)
-       continue;
-      dp = data_skip(dp, data->keys[k].type);
+      /* whoa! this changes the repodataids! */
+      memmove(repo->repodata + i, repo->repodata + i + 1, (repo->nrepodata - 1 - i) * sizeof(Repodata));
+      for (; i < repo->nrepodata - 1; i++)
+       repo->repodata[i].repodataid = i;
+    }
+  repo->nrepodata--;
+  if (repo->nrepodata == 1)
+    {
+      repo->repodata = solv_free(repo->repodata);
+      repo->nrepodata = 0;
     }
-  return 0;
 }
 
-#define BLOB_PAGEBITS 15
-#define BLOB_PAGESIZE (1 << BLOB_PAGEBITS)
+void
+repodata_empty(Repodata *data, int localpool)
+{
+  void (*loadcallback)(Repodata *) = data->loadcallback;
+  int state = data->state;
+  repodata_freedata(data);
+  repodata_initdata(data, data->repo, localpool);
+  data->state = state;
+  data->loadcallback = loadcallback;
+}
 
-static unsigned char *
-load_page_range(Repodata *data, unsigned int pstart, unsigned int pend)
-{
-/* Make sure all pages from PSTART to PEND (inclusive) are loaded,
-   and are consecutive.  Return a pointer to the mapping of PSTART.  */
-  unsigned char buf[BLOB_PAGESIZE];
-  unsigned int i;
-
-  /* Quick check in case all pages are there already and consecutive.  */
-  for (i = pstart; i <= pend; i++)
-    if (data->pages[i].mapped_at == -1
-        || (i > pstart
-           && data->pages[i].mapped_at
-              != data->pages[i-1].mapped_at + BLOB_PAGESIZE))
-      break;
-  if (i > pend)
-    return data->blob_store + data->pages[pstart].mapped_at;
 
-  if (data->pagefd == -1)
-    return 0;
+/***************************************************************
+ * key pool management
+ */
 
-  /* Ensure that we can map the numbers of pages we need at all.  */
-  if (pend - pstart + 1 > data->ncanmap)
-    {
-      unsigned int oldcan = data->ncanmap;
-      data->ncanmap = pend - pstart + 1;
-      if (data->ncanmap < 4)
-        data->ncanmap = 4;
-      data->mapped = sat_realloc2(data->mapped, data->ncanmap, sizeof(data->mapped[0]));
-      memset (data->mapped + oldcan, 0, (data->ncanmap - oldcan) * sizeof (data->mapped[0]));
-      data->blob_store = sat_realloc2(data->blob_store, data->ncanmap, BLOB_PAGESIZE);
-#ifdef DEBUG_PAGING
-      fprintf (stderr, "PAGE: can map %d pages\n", data->ncanmap);
-#endif
+/* this is not so time critical that we need a hash, so we do a simple
+ * linear search */
+Id
+repodata_key2id(Repodata *data, Repokey *key, int create)
+{
+  Id keyid;
+
+  for (keyid = 1; keyid < data->nkeys; keyid++)
+    if (data->keys[keyid].name == key->name && data->keys[keyid].type == key->type)
+      {
+        if ((key->type == REPOKEY_TYPE_CONSTANT || key->type == REPOKEY_TYPE_CONSTANTID) && key->size != data->keys[keyid].size)
+          continue;
+        break;
+      }
+  if (keyid == data->nkeys)
+    {
+      if (!create)
+       return 0;
+      /* allocate new key */
+      data->keys = solv_realloc2(data->keys, data->nkeys + 1, sizeof(Repokey));
+      data->keys[data->nkeys++] = *key;
+      if (data->verticaloffset)
+        {
+          data->verticaloffset = solv_realloc2(data->verticaloffset, data->nkeys, sizeof(Id));
+          data->verticaloffset[data->nkeys - 1] = 0;
+        }
+      data->keybits[(key->name >> 3) & (sizeof(data->keybits) - 1)] |= 1 << (key->name & 7);
     }
+  return keyid;
+}
+
+
+/***************************************************************
+ * schema pool management
+ */
+
+#define SCHEMATA_BLOCK 31
+#define SCHEMATADATA_BLOCK 255
 
-  /* Now search for "cheap" space in our store.  Space is cheap if it's either
-     free (very cheap) or contains pages we search for anyway.  */
+Id
+repodata_schema2id(Repodata *data, Id *schema, int create)
+{
+  int h, len, i;
+  Id *sp, cid;
+  Id *schematahash;
 
-  /* Setup cost array.  */
-  unsigned int cost[data->ncanmap];
-  for (i = 0; i < data->ncanmap; i++)
+  if (!*schema)
+    return 0;  /* XXX: allow empty schema? */
+  if ((schematahash = data->schematahash) == 0)
     {
-      unsigned int pnum = data->mapped[i];
-      if (pnum == 0)
-        cost[i] = 0;
-      else
-        {
-         pnum--;
-         Attrblobpage *p = data->pages + pnum;
-         assert (p->mapped_at != -1);
-         if (pnum >= pstart && pnum <= pend)
-           cost[i] = 1;
-         else
-           cost[i] = 3;
+      data->schematahash = schematahash = solv_calloc(256, sizeof(Id));
+      for (i = 1; i < data->nschemata; i++)
+       {
+         for (sp = data->schemadata + data->schemata[i], h = 0; *sp;)
+           h = h * 7 + *sp++;
+         h &= 255;
+         schematahash[h] = i;
        }
+      data->schemadata = solv_extend_resize(data->schemadata, data->schemadatalen, sizeof(Id), SCHEMATADATA_BLOCK);
+      data->schemata = solv_extend_resize(data->schemata, data->nschemata, sizeof(Id), SCHEMATA_BLOCK);
     }
 
-  /* And search for cheapest space.  */
-  unsigned int best_cost = -1;
-  unsigned int best = 0;
-  unsigned int same_cost = 0;
-  for (i = 0; i + pend - pstart < data->ncanmap; i++)
-    {
-      unsigned int c = cost[i];
-      unsigned int j;
-      for (j = 0; j < pend - pstart + 1; j++)
-        c += cost[i+j];
-      if (c < best_cost)
-        best_cost = c, best = i;
-      else if (c == best_cost)
-        same_cost++;
-      /* A null cost won't become better.  */
-      if (c == 0)
-        break;
+  for (sp = schema, len = 0, h = 0; *sp; len++)
+    h = h * 7 + *sp++;
+  h &= 255;
+  len++;
+
+  cid = schematahash[h];
+  if (cid)
+    {
+      if (!memcmp(data->schemadata + data->schemata[cid], schema, len * sizeof(Id)))
+        return cid;
+      /* cache conflict, do a slow search */
+      for (cid = 1; cid < data->nschemata; cid++)
+        if (!memcmp(data->schemadata + data->schemata[cid], schema, len * sizeof(Id)))
+          return cid;
     }
-  /* If all places have the same cost we would thrash on slot 0.  Avoid
-     this by doing a round-robin strategy in this case.  */
-  if (same_cost == data->ncanmap - pend + pstart - 1)
-    best = data->rr_counter++ % (data->ncanmap - pend + pstart);
-
-  /* So we want to map our pages from [best] to [best+pend-pstart].
-     Use a very simple strategy, which doesn't make the best use of
-     our resources, but works.  Throw away all pages in that range
-     (even ours) then copy around ours (in case they were outside the 
-     range) or read them in.  */
-  for (i = best; i < best + pend - pstart + 1; i++)
-    {
-      unsigned int pnum = data->mapped[i];
-      if (pnum--
-          /* If this page is exactly at the right place already,
-            no need to evict it.  */
-          && pnum != pstart + i - best)
-       {
-         /* Evict this page.  */
-#ifdef DEBUG_PAGING
-         fprintf (stderr, "PAGE: evict page %d from %d\n", pnum, i);
+  /* a new one */
+  if (!create)
+    return 0;
+  data->schemadata = solv_extend(data->schemadata, data->schemadatalen, len, sizeof(Id), SCHEMATADATA_BLOCK);
+  data->schemata = solv_extend(data->schemata, data->nschemata, 1, sizeof(Id), SCHEMATA_BLOCK);
+  /* add schema */
+  memcpy(data->schemadata + data->schemadatalen, schema, len * sizeof(Id));
+  data->schemata[data->nschemata] = data->schemadatalen;
+  data->schemadatalen += len;
+  schematahash[h] = data->nschemata;
+#if 0
+fprintf(stderr, "schema2id: new schema\n");
+#endif
+  return data->nschemata++;
+}
+
+void
+repodata_free_schemahash(Repodata *data)
+{
+  data->schematahash = solv_free(data->schematahash);
+  /* shrink arrays */
+  data->schemata = solv_realloc2(data->schemata, data->nschemata, sizeof(Id));
+  data->schemadata = solv_realloc2(data->schemadata, data->schemadatalen, sizeof(Id));
+}
+
+
+/***************************************************************
+ * dir pool management
+ */
+
+#ifndef HAVE_STRCHRNUL
+static inline const char *strchrnul(const char *str, char x)
+{
+  const char *p = strchr(str, x);
+  return p ? p : str + strlen(str);
+}
 #endif
-         cost[i] = 0;
-         data->mapped[i] = 0;
-         data->pages[pnum].mapped_at = -1;
-       }
-    }
 
-  /* Everything is free now.  Read in the pages we want.  */
-  for (i = pstart; i <= pend; i++)
+#define DIRCACHE_SIZE 41       /* < 1k */
+
+#ifdef DIRCACHE_SIZE
+struct dircache {
+  Id ids[DIRCACHE_SIZE];
+  char str[(DIRCACHE_SIZE * (DIRCACHE_SIZE - 1)) / 2];
+};
+#endif
+
+Id
+repodata_str2dir(Repodata *data, const char *dir, int create)
+{
+  Id id, parent;
+#ifdef DIRCACHE_SIZE
+  const char *dirs;
+#endif
+  const char *dire;
+
+  parent = 0;
+  if (!*dir)
+    return 0;
+  while (*dir == '/' && dir[1] == '/')
+    dir++;
+  if (*dir == '/' && !dir[1])
     {
-      Attrblobpage *p = data->pages + i;
-      unsigned int pnum = i - pstart + best;
-      void *dest = data->blob_store + pnum * BLOB_PAGESIZE;
-      if (p->mapped_at != -1)
-        {
-         if (p->mapped_at != pnum * BLOB_PAGESIZE)
+      if (data->dirpool.ndirs)
+        return 1;
+      return dirpool_add_dir(&data->dirpool, 0, 1, create);
+    }
+#ifdef DIRCACHE_SIZE
+  dirs = dir;
+  if (data->dircache)
+    {
+      int l;
+      struct dircache *dircache = data->dircache;
+      l = strlen(dir);
+      while (l > 0)
+       {
+         if (l < DIRCACHE_SIZE && dircache->ids[l] && !memcmp(dircache->str + l * (l - 1) / 2, dir, l))
            {
-#ifdef DEBUG_PAGING
-             fprintf (stderr, "PAGECOPY: %d to %d\n", i, pnum);
-#endif
-             /* Still mapped somewhere else, so just copy it from there.  */
-             memcpy (dest, data->blob_store + p->mapped_at, BLOB_PAGESIZE);
-             data->mapped[p->mapped_at / BLOB_PAGESIZE] = 0;
+             parent = dircache->ids[l];
+             dir += l;
+             if (!*dir)
+               return parent;
+             while (*dir == '/')
+               dir++;
+             break;
            }
+         while (--l)
+           if (dir[l] == '/')
+             break;
        }
-      else
-        {
-         unsigned int in_len = p->file_size;
-         unsigned int compressed = in_len & 1;
-         in_len >>= 1;
-#ifdef DEBUG_PAGING
-         fprintf (stderr, "PAGEIN: %d to %d", i, pnum);
+    }
 #endif
-          if (pread(data->pagefd, compressed ? buf : dest, in_len, p->file_offset) != in_len)
-           {
-             perror ("mapping pread");
-             return 0;
-           }
-         if (compressed)
+  while (*dir)
+    {
+      dire = strchrnul(dir, '/');
+      if (data->localpool)
+        id = stringpool_strn2id(&data->spool, dir, dire - dir, create);
+      else
+       id = pool_strn2id(data->repo->pool, dir, dire - dir, create);
+      if (!id)
+       return 0;
+      parent = dirpool_add_dir(&data->dirpool, parent, id, create);
+      if (!parent)
+       return 0;
+#ifdef DIRCACHE_SIZE
+      if (!data->dircache)
+       data->dircache = solv_calloc(1, sizeof(struct dircache));
+      if (data->dircache)
+       {
+         int l = dire - dirs;
+         if (l < DIRCACHE_SIZE)
            {
-             unsigned int out_len;
-             out_len = unchecked_decompress_buf(buf, in_len,
-                                                 dest, BLOB_PAGESIZE);
-             if (out_len != BLOB_PAGESIZE && i < data->num_pages - 1)
-               {
-                 fprintf(stderr, "can't decompress\n");
-                 return 0;
-               }
-#ifdef DEBUG_PAGING
-             fprintf (stderr, " (expand %d to %d)", in_len, out_len);
-#endif
+             data->dircache->ids[l] = parent;
+             memcpy(data->dircache->str + l * (l - 1) / 2, dirs, l);
            }
-#ifdef DEBUG_PAGING
-         fprintf (stderr, "\n");
+       }
 #endif
+      if (!*dire)
+       break;
+      dir = dire + 1;
+      while (*dir == '/')
+       dir++;
+    }
+  return parent;
+}
+
+void
+repodata_free_dircache(Repodata *data)
+{
+  data->dircache = solv_free(data->dircache);
+}
+
+const char *
+repodata_dir2str(Repodata *data, Id did, const char *suf)
+{
+  Pool *pool = data->repo->pool;
+  int l = 0;
+  Id parent, comp;
+  const char *comps;
+  char *p;
+
+  if (!did)
+    return suf ? suf : "";
+  parent = did;
+  while (parent)
+    {
+      comp = dirpool_compid(&data->dirpool, parent);
+      comps = stringpool_id2str(data->localpool ? &data->spool : &pool->ss, comp);
+      l += strlen(comps);
+      parent = dirpool_parent(&data->dirpool, parent);
+      if (parent)
+       l++;
+    }
+  if (suf)
+    l += strlen(suf) + 1;
+  p = pool_alloctmpspace(pool, l + 1) + l;
+  *p = 0;
+  if (suf)
+    {
+      p -= strlen(suf);
+      strcpy(p, suf);
+      *--p = '/';
+    }
+  parent = did;
+  while (parent)
+    {
+      comp = dirpool_compid(&data->dirpool, parent);
+      comps = stringpool_id2str(data->localpool ? &data->spool : &pool->ss, comp);
+      l = strlen(comps);
+      p -= l;
+      strncpy(p, comps, l);
+      parent = dirpool_parent(&data->dirpool, parent);
+      if (parent)
+        *--p = '/';
+    }
+  return p;
+}
+
+
+/***************************************************************
+ * data management
+ */
+
+static inline unsigned char *
+data_skip_schema(Repodata *data, unsigned char *dp, Id schema)
+{
+  Id *keyp = data->schemadata + data->schemata[schema];
+  for (; *keyp; keyp++)
+    dp = data_skip_key(data, dp, data->keys + *keyp);
+  return dp;
+}
+
+static unsigned char *
+data_skip_key(Repodata *data, unsigned char *dp, Repokey *key)
+{
+  int nentries, schema;
+  switch(key->type)
+    {
+    case REPOKEY_TYPE_FIXARRAY:
+      dp = data_read_id(dp, &nentries);
+      if (!nentries)
+       return dp;
+      dp = data_read_id(dp, &schema);
+      while (nentries--)
+       dp = data_skip_schema(data, dp, schema);
+      return dp;
+    case REPOKEY_TYPE_FLEXARRAY:
+      dp = data_read_id(dp, &nentries);
+      while (nentries--)
+       {
+         dp = data_read_id(dp, &schema);
+         dp = data_skip_schema(data, dp, schema);
+       }
+      return dp;
+    default:
+      if (key->storage == KEY_STORAGE_INCORE)
+        dp = data_skip(dp, key->type);
+      else if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
+       {
+         dp = data_skip(dp, REPOKEY_TYPE_ID);
+         dp = data_skip(dp, REPOKEY_TYPE_ID);
+       }
+      return dp;
+    }
+}
+
+static unsigned char *
+forward_to_key(Repodata *data, Id keyid, Id *keyp, unsigned char *dp)
+{
+  Id k;
+
+  if (!keyid)
+    return 0;
+  if (data->mainschemaoffsets && dp == data->incoredata + data->mainschemaoffsets[0] && keyp == data->schemadata + data->schemata[data->mainschema])
+    {
+      int i;
+      for (i = 0; (k = *keyp++) != 0; i++)
+        if (k == keyid)
+         return data->incoredata + data->mainschemaoffsets[i];
+      return 0;
+    }
+  while ((k = *keyp++) != 0)
+    {
+      if (k == keyid)
+       return dp;
+      if (data->keys[k].storage == KEY_STORAGE_VERTICAL_OFFSET)
+       {
+         dp = data_skip(dp, REPOKEY_TYPE_ID);  /* skip offset */
+         dp = data_skip(dp, REPOKEY_TYPE_ID);  /* skip length */
+         continue;
        }
-      p->mapped_at = pnum * BLOB_PAGESIZE;
-      data->mapped[pnum] = i + 1;
+      if (data->keys[k].storage != KEY_STORAGE_INCORE)
+       continue;
+      dp = data_skip_key(data, dp, data->keys + k);
     }
-  return data->blob_store + best * BLOB_PAGESIZE;
+  return 0;
 }
 
 static unsigned char *
-make_vertical_available(Repodata *data, Repokey *key, Id off, Id len)
+get_vertical_data(Repodata *data, Repokey *key, Id off, Id len)
 {
   unsigned char *dp;
-  if (!len)
+  if (len <= 0)
     return 0;
   if (off >= data->lastverticaloffset)
     {
       off -= data->lastverticaloffset;
-      if (off + len > data->vincorelen)
+      if ((unsigned int)off + len > data->vincorelen)
        return 0;
       return data->vincore + off;
     }
-  if (off + len > key->size)
+  if ((unsigned int)off + len > key->size)
     return 0;
   /* we now have the offset, go into vertical */
   off += data->verticaloffset[key - data->keys];
-  /* fprintf(stderr, "key %d page %d\n", key->name, off / BLOB_PAGESIZE); */
-  dp = load_page_range(data, off / BLOB_PAGESIZE, (off + len - 1) / BLOB_PAGESIZE);
+  /* fprintf(stderr, "key %d page %d\n", key->name, off / REPOPAGE_BLOBSIZE); */
+  dp = repopagestore_load_page_range(&data->store, off / REPOPAGE_BLOBSIZE, (off + len - 1) / REPOPAGE_BLOBSIZE);
+  data->storestate++;
   if (dp)
-    dp += off % BLOB_PAGESIZE;
+    dp += off % REPOPAGE_BLOBSIZE;
   return dp;
 }
 
 static inline unsigned char *
-get_data(Repodata *data, Repokey *key, unsigned char **dpp)
+get_data(Repodata *data, Repokey *key, unsigned char **dpp, int advance)
 {
   unsigned char *dp = *dpp;
 
@@ -316,8 +504,8 @@ get_data(Repodata *data, Repokey *key, unsigned char **dpp)
     return 0;
   if (key->storage == KEY_STORAGE_INCORE)
     {
-      /* hmm, this is a bit expensive */
-      *dpp = data_skip(dp, key->type);
+      if (advance)
+        *dpp = data_skip_key(data, dp, key);
       return dp;
     }
   else if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
@@ -325,79 +513,144 @@ get_data(Repodata *data, Repokey *key, unsigned char **dpp)
       Id off, len;
       dp = data_read_id(dp, &off);
       dp = data_read_id(dp, &len);
-      *dpp = dp;
-      return make_vertical_available(data, key, off, len);
+      if (advance)
+        *dpp = dp;
+      return get_vertical_data(data, key, off, len);
     }
   return 0;
 }
 
-static inline int
-maybe_load_repodata(Repodata *data, Id *keyid)
+static int
+load_repodata(Repodata *data)
 {
-  if (data->state == REPODATA_STUB)
+  if (data->loadcallback)
     {
-      if (data->loadcallback)
-       {
-         if (keyid)
-           {
-             /* key order may change when loading */
-             int i;
-             Id name = data->keys[*keyid].name;
-             Id type = data->keys[*keyid].type;
-             data->loadcallback(data);
-             if (data->state == REPODATA_AVAILABLE)
-               {
-                 for (i = 1; i < data->nkeys; i++)
-                   if (data->keys[i].name == name && data->keys[i].type == type)
-                     break;
-                 if (i < data->nkeys)
-                   *keyid = i;
-                 else
-                   return 0;
-               }
-           }
-         else
-           data->loadcallback(data);
-       }
-      else
-       data->state = REPODATA_ERROR;
+      data->loadcallback(data);
+      if (data->state == REPODATA_AVAILABLE)
+       return 1;
     }
-  if (data->state == REPODATA_AVAILABLE)
-    return 1;
   data->state = REPODATA_ERROR;
   return 0;
 }
 
-static inline unsigned char*
-entry2data(Repodata *data, Id entry)
+static inline int
+maybe_load_repodata(Repodata *data, Id keyname)
 {
-  if (entry < 0)
-    return data->incoredata + data->extraoffset[-1 - entry];
-  else
-    return data->incoredata + data->incoreoffset[entry];
-}
-
-Id
-repodata_lookup_id(Repodata *data, Id entry, Id keyid)
+  if (keyname && !repodata_precheck_keyname(data, keyname))
+    return 0;  /* do not bother... */
+  switch(data->state)
+    {
+    case REPODATA_STUB:
+      if (keyname)
+       {
+         int i;
+         for (i = 1; i < data->nkeys; i++)
+           if (keyname == data->keys[i].name)
+             break;
+         if (i == data->nkeys)
+           return 0;
+       }
+      return load_repodata(data);
+    case REPODATA_ERROR:
+      return 0;
+    case REPODATA_AVAILABLE:
+    case REPODATA_LOADING:
+      return 1;
+    default:
+      data->state = REPODATA_ERROR;
+      return 0;
+    }
+}
+
+static inline unsigned char *
+solvid2data(Repodata *data, Id solvid, Id *schemap)
+{
+  unsigned char *dp = data->incoredata;
+  if (!dp)
+    return 0;
+  if (solvid == SOLVID_META)
+    dp += 1;   /* offset of "meta" solvable */
+  else if (solvid == SOLVID_POS)
+    {
+      Pool *pool = data->repo->pool;
+      if (data->repo != pool->pos.repo)
+       return 0;
+      if (data != data->repo->repodata + pool->pos.repodataid)
+       return 0;
+      dp += pool->pos.dp;
+      if (pool->pos.dp != 1)
+        {
+          *schemap = pool->pos.schema;
+          return dp;
+       }
+    }
+  else
+    {
+      if (solvid < data->start || solvid >= data->end)
+       return 0;
+      dp += data->incoreoffset[solvid - data->start];
+    }
+  return data_read_id(dp, schemap);
+}
+
+/************************************************************************
+ * data lookup
+ */
+
+static unsigned char *
+find_key_data(Repodata *data, Id solvid, Id keyname, Repokey **keypp)
 {
-  Id schema;
-  Repokey *key;
-  Id id, *keyp;
   unsigned char *dp;
+  Id schema, *keyp, *kp;
+  Repokey *key;
 
-  if (!maybe_load_repodata(data, &keyid))
+  if (!maybe_load_repodata(data, keyname))
     return 0;
-  dp = entry2data(data, entry);
+  dp = solvid2data(data, solvid, &schema);
   if (!dp)
     return 0;
-  dp = data_read_id(dp, &schema);
-  /* make sure the schema of this solvable contains the key */
-  for (keyp = data->schemadata + data->schemata[schema]; *keyp != keyid; keyp++)
-    if (!*keyp)
-      return 0;
-  dp = forward_to_key(data, keyid, schema, dp);
-  key = data->keys + keyid;
-  dp = get_data(data, key, &dp);
+  keyp = data->schemadata + data->schemata[schema];
+  for (kp = keyp; *kp; kp++)
+    if (data->keys[*kp].name == keyname)
+      break;
+  if (!*kp)
+    return 0;
+  *keypp = key = data->keys + *kp;
+  if (key->type == REPOKEY_TYPE_DELETED)
+    return 0;
+  if (key->type == REPOKEY_TYPE_VOID || key->type == REPOKEY_TYPE_CONSTANT || key->type == REPOKEY_TYPE_CONSTANTID)
+    return dp; /* no need to forward... */
+  if (key->storage != KEY_STORAGE_INCORE && key->storage != KEY_STORAGE_VERTICAL_OFFSET)
+    return 0;  /* get_data will not work, no need to forward */
+  dp = forward_to_key(data, *kp, keyp, dp);
+  if (!dp)
+    return 0;
+  return get_data(data, key, &dp, 0);
+}
+
+Id
+repodata_lookup_type(Repodata *data, Id solvid, Id keyname)
+{
+  Id schema, *keyp, *kp;
+  if (!maybe_load_repodata(data, keyname))
+    return 0;
+  if (!solvid2data(data, solvid, &schema))
+    return 0;
+  keyp = data->schemadata + data->schemata[schema];
+  for (kp = keyp; *kp; kp++)
+    if (data->keys[*kp].name == keyname)
+      return data->keys[*kp].type;
+  return 0;
+}
+
+Id
+repodata_lookup_id(Repodata *data, Id solvid, Id keyname)
+{
+  unsigned char *dp;
+  Repokey *key;
+  Id id;
+
+  dp = find_key_data(data, solvid, keyname, &key);
   if (!dp)
     return 0;
   if (key->type == REPOKEY_TYPE_CONSTANTID)
@@ -409,166 +662,359 @@ repodata_lookup_id(Repodata *data, Id entry, Id keyid)
 }
 
 const char *
-repodata_lookup_str(Repodata *data, Id entry, Id keyid)
+repodata_lookup_str(Repodata *data, Id solvid, Id keyname)
 {
-  Id schema;
-  Repokey *key;
-  Id id, *keyp;
   unsigned char *dp;
+  Repokey *key;
+  Id id;
 
-  if (!maybe_load_repodata(data, &keyid))
-    return 0;
-
-  dp = entry2data(data, entry);
-  if (!dp)
-    return 0;
-  dp = data_read_id(dp, &schema);
-  /* make sure the schema of this solvable contains the key */
-  for (keyp = data->schemadata + data->schemata[schema]; *keyp != keyid; keyp++)
-    if (!*keyp)
-      return 0;
-  dp = forward_to_key(data, keyid, schema, dp);
-  key = data->keys + keyid;
-  dp = get_data(data, key, &dp);
+  dp = find_key_data(data, solvid, keyname, &key);
   if (!dp)
     return 0;
   if (key->type == REPOKEY_TYPE_STR)
     return (const char *)dp;
   if (key->type == REPOKEY_TYPE_CONSTANTID)
-    return id2str(data->repo->pool, key->size);
-  if (key->type == REPOKEY_TYPE_ID)
+    id = key->size;
+  else if (key->type == REPOKEY_TYPE_ID)
     dp = data_read_id(dp, &id);
   else
     return 0;
   if (data->localpool)
-    return data->spool.stringspace + data->spool.strings[id];
-  return id2str(data->repo->pool, id);
+    return stringpool_id2str(&data->spool, id);
+  return pool_id2str(data->repo->pool, id);
 }
 
 int
-repodata_lookup_num(Repodata *data, Id entry, Id keyid, unsigned int *value)
+repodata_lookup_num(Repodata *data, Id solvid, Id keyname, unsigned long long *value)
 {
-  Id schema;
-  Repokey *key;
-  Id *keyp;
-  KeyValue kv;
   unsigned char *dp;
+  Repokey *key;
+  unsigned int high, low;
 
   *value = 0;
-
-  if (!maybe_load_repodata(data, &keyid))
-    return 0;
-
-  dp = entry2data(data, entry);
-  if (!dp)
-    return 0;
-  dp = data_read_id(dp, &schema);
-  /* make sure the schema of this solvable contains the key */
-  for (keyp = data->schemadata + data->schemata[schema]; *keyp != keyid; keyp++)
-    if (!*keyp)
-      return 0;
-  dp = forward_to_key(data, keyid, schema, dp);
-  key = data->keys + keyid;
-  dp = get_data(data, key, &dp);
+  dp = find_key_data(data, solvid, keyname, &key);
   if (!dp)
     return 0;
-  if (key->type == REPOKEY_TYPE_NUM
-      || key->type == REPOKEY_TYPE_U32
-      || key->type == REPOKEY_TYPE_CONSTANT)
+  switch (key->type)
     {
-      dp = data_fetch(dp, &kv, key);
-      *value = kv.num;
+    case REPOKEY_TYPE_NUM:
+      data_read_num64(dp, &low, &high);
+      *value = (unsigned long long)high << 32 | low;
+      return 1;
+    case REPOKEY_TYPE_U32:
+      data_read_u32(dp, &low);
+      *value = low;
       return 1;
+    case REPOKEY_TYPE_CONSTANT:
+      *value = key->size;
+      return 1;
+    default:
+      return 0;
     }
-  return 0;
 }
 
 int
-repodata_lookup_void(Repodata *data, Id entry, Id keyid)
+repodata_lookup_void(Repodata *data, Id solvid, Id keyname)
 {
   Id schema;
   Id *keyp;
   unsigned char *dp;
-  if (!maybe_load_repodata(data, &keyid))
+
+  if (!maybe_load_repodata(data, keyname))
     return 0;
-  dp = entry2data(data, entry);
+  dp = solvid2data(data, solvid, &schema);
   if (!dp)
     return 0;
-  dp = data_read_id(dp, &schema);
-  for (keyp = data->schemadata + data->schemata[schema]; *keyp != keyid; keyp++)
-    if (!*keyp)
-      return 0;
-  return 1;
+  /* can't use find_key_data as we need to test the type */
+  for (keyp = data->schemadata + data->schemata[schema]; *keyp; keyp++)
+    if (data->keys[*keyp].name == keyname && data->keys[*keyp].type == REPOKEY_TYPE_VOID)
+      return 1;
+  return 0;
 }
 
 const unsigned char *
-repodata_lookup_bin_checksum(Repodata *data, Id entry, Id keyid, Id *typep)
+repodata_lookup_bin_checksum(Repodata *data, Id solvid, Id keyname, Id *typep)
 {
-  Id schema;
-  Id *keyp;
-  Repokey *key;
   unsigned char *dp;
+  Repokey *key;
 
-  if (!maybe_load_repodata(data, &keyid))
-    return 0;
-  dp = entry2data(data, entry);
+  dp = find_key_data(data, solvid, keyname, &key);
   if (!dp)
     return 0;
-  dp = data_read_id(dp, &schema);
-  for (keyp = data->schemadata + data->schemata[schema]; *keyp != keyid; keyp++)
-    if (!*keyp)
+  switch (key->type)
+    {
+    case_CHKSUM_TYPES:
+      break;
+    default:
       return 0;
-  dp = forward_to_key(data, keyid, schema, dp);
-  key = data->keys + keyid;
+    }
   *typep = key->type;
-  return get_data(data, key, &dp);
+  return dp;
+}
+
+int
+repodata_lookup_idarray(Repodata *data, Id solvid, Id keyname, Queue *q)
+{
+  unsigned char *dp;
+  Repokey *key;
+  Id id;
+  int eof = 0;
+
+  queue_empty(q);
+  dp = find_key_data(data, solvid, keyname, &key);
+  if (!dp)
+    return 0;
+  if (key->type != REPOKEY_TYPE_IDARRAY)
+    return 0;
+  for (;;)
+    {
+      dp = data_read_ideof(dp, &id, &eof);
+      queue_push(q, id);
+      if (eof)
+       break;
+    }
+  return 1;
+}
+
+const void *
+repodata_lookup_binary(Repodata *data, Id solvid, Id keyname, int *lenp)
+{
+  unsigned char *dp;
+  Repokey *key;
+  Id len;
+
+  dp = find_key_data(data, solvid, keyname, &key);
+  if (!dp || key->type != REPOKEY_TYPE_BINARY)
+    {
+      *lenp = 0;
+      return 0;
+    }
+  dp = data_read_id(dp, &len);
+  *lenp = len;
+  return dp;
 }
 
+Id
+repodata_globalize_id(Repodata *data, Id id, int create)
+{
+  if (!id || !data || !data->localpool)
+    return id;
+  return pool_str2id(data->repo->pool, stringpool_id2str(&data->spool, id), create);
+}
+
+Id
+repodata_localize_id(Repodata *data, Id id, int create)
+{
+  if (!id || !data || !data->localpool)
+    return id;
+  return stringpool_str2id(&data->spool, pool_id2str(data->repo->pool, id), create);
+}
+
+Id
+repodata_translate_id(Repodata *data, Repodata *fromdata, Id id, int create)
+{
+  if (!id || !data || !fromdata)
+    return id;
+  if (!data->localpool || !fromdata->localpool)
+    {
+      if (fromdata->localpool)
+       id = repodata_globalize_id(fromdata, id, create);
+      if (data->localpool)
+       id = repodata_localize_id(data, id, create);
+      return id;
+    }
+  /* localpool is set in both data and fromdata */
+  return stringpool_str2id(&data->spool, stringpool_id2str(&fromdata->spool, id), create);
+}
+
+Id
+repodata_lookup_id_uninternalized(Repodata *data, Id solvid, Id keyname, Id voidid)
+{
+  Id *ap;
+  if (!data->attrs)
+    return 0;
+  ap = data->attrs[solvid - data->start];
+  if (!ap)
+    return 0;
+  for (; *ap; ap += 2)
+    {
+      if (data->keys[*ap].name != keyname)
+       continue;
+      if (data->keys[*ap].type == REPOKEY_TYPE_VOID)
+       return voidid;
+      if (data->keys[*ap].type == REPOKEY_TYPE_ID)
+       return ap[1];
+      return 0;
+    }
+  return 0;
+}
+
+
+/************************************************************************
+ * data search
+ */
+
+
+const char *
+repodata_stringify(Pool *pool, Repodata *data, Repokey *key, KeyValue *kv, int flags)
+{
+  switch (key->type)
+    {
+    case REPOKEY_TYPE_ID:
+    case REPOKEY_TYPE_CONSTANTID:
+    case REPOKEY_TYPE_IDARRAY:
+      if (data && data->localpool)
+       kv->str = stringpool_id2str(&data->spool, kv->id);
+      else
+       kv->str = pool_id2str(pool, kv->id);
+      if ((flags & SEARCH_SKIP_KIND) != 0 && key->storage == KEY_STORAGE_SOLVABLE)
+       {
+         const char *s;
+         for (s = kv->str; *s >= 'a' && *s <= 'z'; s++)
+           ;
+         if (*s == ':' && s > kv->str)
+           kv->str = s + 1;
+       }
+      return kv->str;
+    case REPOKEY_TYPE_STR:
+      return kv->str;
+    case REPOKEY_TYPE_DIRSTRARRAY:
+      if (!(flags & SEARCH_FILES))
+       return kv->str; /* match just the basename */
+      if (kv->num)
+       return kv->str; /* already stringified */
+      /* Put the full filename into kv->str.  */
+      kv->str = repodata_dir2str(data, kv->id, kv->str);
+      kv->num = 1;     /* mark stringification */
+      return kv->str;
+    case_CHKSUM_TYPES:
+      if (!(flags & SEARCH_CHECKSUMS))
+       return 0;       /* skip em */
+      if (kv->num)
+       return kv->str; /* already stringified */
+      kv->str = repodata_chk2str(data, key->type, (const unsigned char *)kv->str);
+      kv->num = 1;     /* mark stringification */
+      return kv->str;
+    default:
+      return 0;
+    }
+}
+
+
+struct subschema_data {
+  Solvable *s;
+  void *cbdata;
+  KeyValue *parent;
+};
+
+/* search a specific repodata */
 void
-repodata_search(Repodata *data, Id entry, Id keyname, int (*callback)(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv), void *cbdata)
+repodata_search(Repodata *data, Id solvid, Id keyname, int flags, int (*callback)(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv), void *cbdata)
 {
   Id schema;
   Repokey *key;
-  Id k, keyid, *kp, *keyp;
+  Id keyid, *kp, *keyp;
   unsigned char *dp, *ddp;
   int onekey = 0;
   int stop;
   KeyValue kv;
+  Solvable *s;
 
-  if (entry < 0
-      || !maybe_load_repodata(data, 0))
-    return;
-
-  dp = entry2data(data, entry);
-  if (!dp)
+  if (!maybe_load_repodata(data, keyname))
     return;
-  dp = data_read_id(dp, &schema);
+  if (solvid == SOLVID_SUBSCHEMA)
+    {
+      struct subschema_data *subd = cbdata;
+      cbdata = subd->cbdata;
+      s = subd->s;
+      schema = subd->parent->id;
+      dp = (unsigned char *)subd->parent->str;
+      kv.parent = subd->parent;
+    }
+  else
+    {
+      schema = 0;
+      dp = solvid2data(data, solvid, &schema);
+      if (!dp)
+       return;
+      s = data->repo->pool->solvables + solvid;
+      kv.parent = 0;
+    }
   keyp = data->schemadata + data->schemata[schema];
   if (keyname)
     {
-      /* search in a specific key */
-      for (kp = keyp; (k = *kp++) != 0; )
-       if (data->keys[k].name == keyname)
+      /* search for a specific key */
+      for (kp = keyp; *kp; kp++)
+       if (data->keys[*kp].name == keyname)
          break;
-      if (k == 0)
+      if (!*kp)
        return;
-      dp = forward_to_key(data, k, schema, dp);
+      dp = forward_to_key(data, *kp, keyp, dp);
       if (!dp)
        return;
-      keyp = kp - 1;
+      keyp = kp;
       onekey = 1;
     }
   while ((keyid = *keyp++) != 0)
     {
       stop = 0;
       key = data->keys + keyid;
-      ddp = get_data(data, key, &dp);
+      ddp = get_data(data, key, &dp, *keyp ? 1 : 0);
+
+      if (key->type == REPOKEY_TYPE_DELETED)
+       continue;
+      if (key->type == REPOKEY_TYPE_FLEXARRAY || key->type == REPOKEY_TYPE_FIXARRAY)
+       {
+         struct subschema_data subd;
+         int nentries;
+         Id schema = 0;
+
+         subd.cbdata = cbdata;
+         subd.s = s;
+         subd.parent = &kv;
+         ddp = data_read_id(ddp, &nentries);
+         kv.num = nentries;
+         kv.entry = 0;
+         kv.eof = 0;
+          while (ddp && nentries > 0)
+           {
+             if (!--nentries)
+               kv.eof = 1;
+             if (key->type == REPOKEY_TYPE_FLEXARRAY || !kv.entry)
+               ddp = data_read_id(ddp, &schema);
+             kv.id = schema;
+             kv.str = (char *)ddp;
+             stop = callback(cbdata, s, data, key, &kv);
+             if (stop > SEARCH_NEXT_KEY)
+               return;
+             if (stop && stop != SEARCH_ENTERSUB)
+               break;
+             if ((flags & SEARCH_SUB) != 0 || stop == SEARCH_ENTERSUB)
+               repodata_search(data, SOLVID_SUBSCHEMA, 0, flags, callback, &subd);
+             ddp = data_skip_schema(data, ddp, schema);
+             kv.entry++;
+           }
+         if (!nentries && (flags & SEARCH_ARRAYSENTINEL) != 0)
+           {
+             /* sentinel */
+             kv.eof = 2;
+             kv.str = (char *)ddp;
+             stop = callback(cbdata, s, data, key, &kv);
+             if (stop > SEARCH_NEXT_KEY)
+               return;
+           }
+         if (onekey)
+           return;
+         continue;
+       }
+      kv.entry = 0;
       do
        {
          ddp = data_fetch(ddp, &kv, key);
          if (!ddp)
            break;
-         stop = callback(cbdata, data->repo->pool->solvables + data->start + entry, data, key, &kv);
+         stop = callback(cbdata, s, data, key, &kv);
+         kv.entry++;
        }
       while (!kv.eof && !stop);
       if (onekey || stop > SEARCH_NEXT_KEY)
@@ -576,475 +1022,1013 @@ repodata_search(Repodata *data, Id entry, Id keyname, int (*callback)(void *cbda
     }
 }
 
-static void
-dataiterator_newdata(Dataiterator *di)
+void
+repodata_setpos_kv(Repodata *data, KeyValue *kv)
 {
-  Id keyname = di->keyname;
-  Repodata *data = di->data;
-  di->nextkeydp = 0;
+  Pool *pool = data->repo->pool;
+  if (!kv)
+    pool_clear_pos(pool);
+  else
+    {
+      pool->pos.repo = data->repo;
+      pool->pos.repodataid = data - data->repo->repodata;
+      pool->pos.dp = (unsigned char *)kv->str - data->incoredata;
+      pool->pos.schema = kv->id;
+    }
+}
 
-  if (data->state == REPODATA_STUB)
+/************************************************************************
+ * data iterator functions
+ */
+
+static inline Id *
+solvabledata_fetch(Solvable *s, KeyValue *kv, Id keyname)
+{
+  kv->id = keyname;
+  switch (keyname)
     {
-      if (keyname)
+    case SOLVABLE_NAME:
+      kv->eof = 1;
+      return &s->name;
+    case SOLVABLE_ARCH:
+      kv->eof = 1;
+      return &s->arch;
+    case SOLVABLE_EVR:
+      kv->eof = 1;
+      return &s->evr;
+    case SOLVABLE_VENDOR:
+      kv->eof = 1;
+      return &s->vendor;
+    case SOLVABLE_PROVIDES:
+      kv->eof = 0;
+      return s->provides ? s->repo->idarraydata + s->provides : 0;
+    case SOLVABLE_OBSOLETES:
+      kv->eof = 0;
+      return s->obsoletes ? s->repo->idarraydata + s->obsoletes : 0;
+    case SOLVABLE_CONFLICTS:
+      kv->eof = 0;
+      return s->conflicts ? s->repo->idarraydata + s->conflicts : 0;
+    case SOLVABLE_REQUIRES:
+      kv->eof = 0;
+      return s->requires ? s->repo->idarraydata + s->requires : 0;
+    case SOLVABLE_RECOMMENDS:
+      kv->eof = 0;
+      return s->recommends ? s->repo->idarraydata + s->recommends : 0;
+    case SOLVABLE_SUPPLEMENTS:
+      kv->eof = 0;
+      return s->supplements ? s->repo->idarraydata + s->supplements : 0;
+    case SOLVABLE_SUGGESTS:
+      kv->eof = 0;
+      return s->suggests ? s->repo->idarraydata + s->suggests : 0;
+    case SOLVABLE_ENHANCES:
+      kv->eof = 0;
+      return s->enhances ? s->repo->idarraydata + s->enhances : 0;
+    case RPM_RPMDBID:
+      kv->eof = 1;
+      return s->repo->rpmdbid ? s->repo->rpmdbid + (s - s->repo->pool->solvables - s->repo->start) : 0;
+    default:
+      return 0;
+    }
+}
+
+int
+datamatcher_init(Datamatcher *ma, const char *match, int flags)
+{
+  match = match ? solv_strdup(match) : 0;
+  ma->match = match;
+  ma->flags = flags;
+  ma->error = 0;
+  ma->matchdata = 0;
+  if ((flags & SEARCH_STRINGMASK) == SEARCH_REGEX)
+    {
+      ma->matchdata = solv_calloc(1, sizeof(regex_t));
+      ma->error = regcomp((regex_t *)ma->matchdata, match, REG_EXTENDED | REG_NOSUB | REG_NEWLINE | ((flags & SEARCH_NOCASE) ? REG_ICASE : 0));
+      if (ma->error)
+       {
+         solv_free(ma->matchdata);
+         ma->flags = (flags & ~SEARCH_STRINGMASK) | SEARCH_ERROR;
+       }
+    }
+  if ((flags & SEARCH_FILES) != 0 && match)
+    {
+      /* prepare basename check */
+      if ((flags & SEARCH_STRINGMASK) == SEARCH_STRING || (flags & SEARCH_STRINGMASK) == SEARCH_STRINGEND)
+       {
+         const char *p = strrchr(match, '/');
+         ma->matchdata = (void *)(p ? p + 1 : match);
+       }
+      else if ((flags & SEARCH_STRINGMASK) == SEARCH_GLOB)
        {
-         int j;
-         for (j = 1; j < data->nkeys; j++)
-           if (keyname == data->keys[j].name)
+         const char *p;
+         for (p = match + strlen(match) - 1; p >= match; p--)
+           if (*p == '[' || *p == ']' || *p == '*' || *p == '?' || *p == '/')
              break;
-         if (j == data->nkeys)
-           return;
+         ma->matchdata = (void *)(p + 1);
        }
-      /* load it */
-      if (data->loadcallback)
-       data->loadcallback(data);
+    }
+  return ma->error;
+}
+
+void
+datamatcher_free(Datamatcher *ma)
+{
+  if (ma->match)
+    ma->match = solv_free((char *)ma->match);
+  if ((ma->flags & SEARCH_STRINGMASK) == SEARCH_REGEX && ma->matchdata)
+    {
+      regfree(ma->matchdata);
+      solv_free(ma->matchdata);
+    }
+  ma->matchdata = 0;
+}
+
+int
+datamatcher_match(Datamatcher *ma, const char *str)
+{
+  int l;
+  switch ((ma->flags & SEARCH_STRINGMASK))
+    {
+    case SEARCH_SUBSTRING:
+      if (ma->flags & SEARCH_NOCASE)
+       return strcasestr(str, ma->match) != 0;
       else
-       data->state = REPODATA_ERROR;
+       return strstr(str, ma->match) != 0;
+    case SEARCH_STRING:
+      if (ma->flags & SEARCH_NOCASE)
+       return !strcasecmp(ma->match, str);
+      else
+       return !strcmp(ma->match, str);
+    case SEARCH_STRINGSTART:
+      if (ma->flags & SEARCH_NOCASE)
+        return !strncasecmp(ma->match, str, strlen(ma->match));
+      else
+        return !strncmp(ma->match, str, strlen(ma->match));
+    case SEARCH_STRINGEND:
+      l = strlen(str) - strlen(ma->match);
+      if (l < 0)
+       return 0;
+      if (ma->flags & SEARCH_NOCASE)
+       return !strcasecmp(ma->match, str + l);
+      else
+       return !strcmp(ma->match, str + l);
+    case SEARCH_GLOB:
+      return !fnmatch(ma->match, str, (ma->flags & SEARCH_NOCASE) ? FNM_CASEFOLD : 0);
+    case SEARCH_REGEX:
+      return !regexec((const regex_t *)ma->matchdata, str, 0, NULL, 0);
+    default:
+      return 0;
     }
-  if (data->state == REPODATA_ERROR)
-    return;
+}
 
-  Id schema;
-  unsigned char *dp = data->incoredata;
-  if (!dp)
-    return;
-  if (di->solvid >= 0)
-    dp += data->incoreoffset[di->solvid - data->start];
+/* check if the matcher can match the provides basename */
+
+int
+datamatcher_checkbasename(Datamatcher *ma, const char *basename)
+{
+  int l;
+  const char *match = ma->matchdata;
+  if (!match)
+    return 1;
+  switch (ma->flags & SEARCH_STRINGMASK)
+    {
+    case SEARCH_STRING:
+      break;
+    case SEARCH_STRINGEND:
+      if (match != ma->match)
+       break;          /* had slash, do exact match on basename */
+      /* FALLTHROUGH */
+    case SEARCH_GLOB:
+      /* check if the basename ends with match */
+      l = strlen(basename) - strlen(match);
+      if (l < 0)
+       return 0;
+      basename += l;
+      break;
+    default:
+      return 1;        /* maybe matches */
+    }
+  if ((ma->flags & SEARCH_NOCASE) != 0)
+    return !strcasecmp(match, basename);
   else
-    dp += data->extraoffset[-1 - di->solvid - data->extrastart];
-  dp = data_read_id(dp, &schema);
-  Id *keyp = data->schemadata + data->schemata[schema];
-  if (keyname)
+    return !strcmp(match, basename);
+}
+
+int
+repodata_filelistfilter_matches(Repodata *data, const char *str)
+{
+  /* '.*bin\/.*', '^\/etc\/.*', '^\/usr\/lib\/sendmail$' */
+  /* for now hardcoded */
+  if (strstr(str, "bin/"))
+    return 1;
+  if (!strncmp(str, "/etc/", 5))
+    return 1;
+  if (!strcmp(str, "/usr/lib/sendmail"))
+    return 1;
+  return 0;
+}
+
+
+enum {
+  di_bye,
+
+  di_enterrepo,
+  di_entersolvable,
+  di_enterrepodata,
+  di_enterschema,
+  di_enterkey,
+
+  di_nextattr,
+  di_nextkey,
+  di_nextrepodata,
+  di_nextsolvable,
+  di_nextrepo,
+
+  di_enterarray,
+  di_nextarrayelement,
+
+  di_entersub,
+  di_leavesub,
+
+  di_nextsolvablekey,
+  di_entersolvablekey,
+  di_nextsolvableattr
+};
+
+/* see dataiterator.h for documentation */
+int
+dataiterator_init(Dataiterator *di, Pool *pool, Repo *repo, Id p, Id keyname, const char *match, int flags)
+{
+  memset(di, 0, sizeof(*di));
+  di->pool = pool;
+  di->flags = flags & ~SEARCH_THISSOLVID;
+  if (!pool || (repo && repo->pool != pool))
     {
-      Id k, *kp;
-      /* search in a specific key */
-      for (kp = keyp; (k = *kp++) != 0; )
-       if (data->keys[k].name == keyname)
-         break;
-      if (k == 0)
-       return;
-      dp = forward_to_key(data, k, schema, dp);
-      if (!dp)
-       return;
-      keyp = kp - 1;
+      di->state = di_bye;
+      return -1;
     }
-  Id keyid = *keyp++;
-  if (!keyid)
-    return;
+  if (match)
+    {
+      int error;
+      if ((error = datamatcher_init(&di->matcher, match, flags)) != 0)
+       {
+         di->state = di_bye;
+         return error;
+       }
+    }
+  di->keyname = keyname;
+  di->keynames[0] = keyname;
+  dataiterator_set_search(di, repo, p);
+  return 0;
+}
+
+void
+dataiterator_init_clone(Dataiterator *di, Dataiterator *from)
+{
+  *di = *from;
+  if (di->dupstr)
+    {
+      if (di->dupstr == di->kv.str)
+        di->dupstr = solv_memdup(di->dupstr, di->dupstrn);
+      else
+       {
+         di->dupstr = 0;
+         di->dupstrn = 0;
+       }
+    }
+  memset(&di->matcher, 0, sizeof(di->matcher));
+  if (from->matcher.match)
+    datamatcher_init(&di->matcher, from->matcher.match, from->matcher.flags);
+  if (di->nparents)
+    {
+      /* fix pointers */
+      int i;
+      for (i = 1; i < di->nparents; i++)
+       di->parents[i].kv.parent = &di->parents[i - 1].kv;
+      di->kv.parent = &di->parents[di->nparents - 1].kv;
+    }
+}
 
-  di->data = data;
-  di->key = di->data->keys + keyid;
+int
+dataiterator_set_match(Dataiterator *di, const char *match, int flags)
+{
+  di->flags = (flags & ~SEARCH_THISSOLVID) | (di->flags & SEARCH_THISSOLVID);
+  datamatcher_free(&di->matcher);
+  memset(&di->matcher, 0, sizeof(di->matcher));
+  if (match)
+    {
+      int error;
+      if ((error = datamatcher_init(&di->matcher, match, flags)) != 0)
+       {
+         di->state = di_bye;
+         return error;
+       }
+    }
+  return 0;
+}
+
+void
+dataiterator_set_search(Dataiterator *di, Repo *repo, Id p)
+{
+  di->repo = repo;
+  di->repoid = 0;
+  di->flags &= ~SEARCH_THISSOLVID;
+  di->nparents = 0;
+  di->rootlevel = 0;
+  di->repodataid = 1;
+  if (!di->pool->urepos)
+    {
+      di->state = di_bye;
+      return;
+    }
+  if (!repo)
+    {
+      di->repoid = 1;
+      di->repo = di->pool->repos[di->repoid];
+    }
+  di->state = di_enterrepo;
+  if (p)
+    dataiterator_jump_to_solvid(di, p);
+}
+
+void
+dataiterator_set_keyname(Dataiterator *di, Id keyname)
+{
+  di->nkeynames = 0;
+  di->keyname = keyname;
+  di->keynames[0] = keyname;
+}
+
+void
+dataiterator_prepend_keyname(Dataiterator *di, Id keyname)
+{
+  int i;
+
+  if (di->nkeynames >= sizeof(di->keynames)/sizeof(*di->keynames) - 2)
+    {
+      di->state = di_bye;      /* sorry */
+      return;
+    }
+  for (i = di->nkeynames + 1; i > 0; i--)
+    di->keynames[i] = di->keynames[i - 1];
+  di->keynames[0] = di->keyname = keyname;
+  di->nkeynames++;
+}
+
+void
+dataiterator_free(Dataiterator *di)
+{
+  if (di->matcher.match)
+    datamatcher_free(&di->matcher);
+  if (di->dupstr)
+    solv_free(di->dupstr);
+}
+
+static unsigned char *
+dataiterator_find_keyname(Dataiterator *di, Id keyname)
+{
+  Id *keyp;
+  Repokey *keys = di->data->keys, *key;
+  unsigned char *dp;
+
+  for (keyp = di->keyp; *keyp; keyp++)
+    if (keys[*keyp].name == keyname)
+      break;
+  if (!*keyp)
+    return 0;
+  key = keys + *keyp;
+  if (key->type == REPOKEY_TYPE_DELETED)
+    return 0;
+  if (key->storage != KEY_STORAGE_INCORE && key->storage != KEY_STORAGE_VERTICAL_OFFSET)
+    return 0;          /* get_data will not work, no need to forward */
+  dp = forward_to_key(di->data, *keyp, di->keyp, di->dp);
+  if (!dp)
+    return 0;
   di->keyp = keyp;
-  di->dp = 0;
-
-  di->nextkeydp = dp;
-  di->dp = get_data(di->data, di->key, &di->nextkeydp);
-  di->kv.eof = 0;
+  return dp;
 }
 
-void
-dataiterator_init(Dataiterator *di, Repo *repo, Id p, Id keyname,
-                 const char *match, int flags)
+static inline int
+is_filelist_extension(Repodata *data)
 {
-  di->flags = flags;
-  if (p)
-    {
-      di->solvid = p;
-      di->flags |= __SEARCH_ONESOLVABLE;
-      di->data = repo->repodata - 1;
-      if (flags & SEARCH_NO_STORAGE_SOLVABLE)
-       di->state = 0;
-      else
-       di->state = 1;
-    }
-  else
-    {
-      di->solvid = repo->start - 1;
-      if (di->solvid < 0)
-       {
-         fprintf(stderr, "A repo contains the NULL solvable!\n");
-         exit(1);
-       }
-      di->data = repo->repodata + repo->nrepodata - 1;
-      di->state = 0;
-    }
-
-  di->match = match;
-  if ((di->flags & SEARCH_STRINGMASK) == SEARCH_REGEX)
-    {
-      if (di->match)
-        {
-          /* We feed multiple lines eventually (e.g. authors or descriptions),
-             so set REG_NEWLINE. */
-          di->regex_err =
-            regcomp(&di->regex, di->match,
-              REG_EXTENDED | REG_NOSUB | REG_NEWLINE
-              | ((di->flags & SEARCH_NOCASE) ? REG_ICASE : 0));
-#if 0
-          if (di->regex_err != 0)
-            {
-              fprintf(stderr, "Given regex failed to compile: %s\n", di->match);
-              fprintf(stderr, "regcomp error code: %d\n", di->regex_err);
-              exit(1);
-            }
-#else
-        }
-      else
-        {
-          di->flags |= (di->flags & SEARCH_STRINGMASK) | SEARCH_STRING;
-          di->regex_err = 0;
-#endif
-        }
-    }
-
-  di->keyname = keyname;
-  static Id zeroid = 0;
-  di->keyp = &zeroid;
-  di->kv.eof = 1;
-  di->repo = repo;
-  di->idp = 0;
+  int j;
+  if (!repodata_precheck_keyname(data, SOLVABLE_FILELIST))
+    return 0;
+  for (j = 1; j < data->nkeys; j++)
+    if (data->keys[j].name == SOLVABLE_FILELIST)
+      break;
+  if (j == data->nkeys)
+    return 0;
+  if (data->state != REPODATA_AVAILABLE)
+    return 1;
+  for (j = 1; j < data->nkeys; j++)
+    if (data->keys[j].name != REPOSITORY_SOLVABLES && data->keys[j].name != SOLVABLE_FILELIST)
+      return 0;
+  return 1;
 }
 
-/* FIXME factor and merge with repo_matchvalue */
 static int
-dataiterator_match_int_real(Dataiterator *di, int flags, const void *vmatch)
+dataiterator_filelistcheck(Dataiterator *di)
 {
-  KeyValue *kv = &di->kv;
-  const char *match = vmatch;
-  if ((flags & SEARCH_STRINGMASK) != 0)
+  int j;
+  int needcomplete = 0;
+  Repodata *data = di->data;
+
+  if ((di->flags & SEARCH_COMPLETE_FILELIST) != 0)
+    if (!di->matcher.match
+       || ((di->matcher.flags & (SEARCH_STRINGMASK|SEARCH_NOCASE)) != SEARCH_STRING
+           && (di->matcher.flags & (SEARCH_STRINGMASK|SEARCH_NOCASE)) != SEARCH_GLOB)
+       || !repodata_filelistfilter_matches(data, di->matcher.match))
+      needcomplete = 1;
+  if (data->state != REPODATA_AVAILABLE)
+    return needcomplete ? 1 : 0;
+  if (!needcomplete)
     {
-      switch (di->key->type)
-       {
-       case REPOKEY_TYPE_ID:
-       case REPOKEY_TYPE_IDARRAY:
-         if (di->data && di->data->localpool)
-           kv->str = stringpool_id2str(&di->data->spool, kv->id);
-         else
-           kv->str = id2str(di->repo->pool, kv->id);
+      /* we don't need the complete filelist, so ignore all stubs */
+      if (data->repo->nrepodata == 2)
+       return 1;
+      for (j = 1; j < data->nkeys; j++)
+       if (data->keys[j].name != REPOSITORY_SOLVABLES && data->keys[j].name != SOLVABLE_FILELIST)
+         return 1;
+      return 0;
+    }
+  else
+    {
+      /* we need the complete filelist. check if we habe a filtered filelist and there's
+       * a extension with the complete filelist later on */
+      for (j = 1; j < data->nkeys; j++)
+       if (data->keys[j].name == SOLVABLE_FILELIST)
          break;
-       case REPOKEY_TYPE_STR:
+      if (j == data->nkeys)
+       return 0;       /* does not have filelist */
+      for (j = 1; j < data->nkeys; j++)
+       if (data->keys[j].name != REPOSITORY_SOLVABLES && data->keys[j].name != SOLVABLE_FILELIST)
          break;
-       default:
-         return 0;
-       }
-      switch ((flags & SEARCH_STRINGMASK))
+      if (j == data->nkeys)
+       return 1;       /* this is the externsion */
+      while (data - data->repo->repodata + 1 < data->repo->nrepodata)
        {
-         case SEARCH_SUBSTRING:
-           if (flags & SEARCH_NOCASE)
-             {
-               if (!strcasestr(kv->str, match))
-                 return 0;
-             }
-           else
-             {
-               if (!strstr(kv->str, match))
-                 return 0;
-             }
-           break;
-         case SEARCH_STRING:
-           if (flags & SEARCH_NOCASE)
-             {
-               if (strcasecmp(match, kv->str))
-                 return 0;
-             }
-           else
-             {
-               if (strcmp(match, kv->str))
-                 return 0;
-             }
-           break;
-         case SEARCH_GLOB:
-           if (fnmatch(match, kv->str, (flags & SEARCH_NOCASE) ? FNM_CASEFOLD : 0))
-             return 0;
-           break;
-         case SEARCH_REGEX:
-           if (regexec((const regex_t *)vmatch, kv->str, 0, NULL, 0))
-             return 0;
-           break;
-         default:
+         data++;
+         if (is_filelist_extension(data))
            return 0;
        }
+      return 1;
     }
-  return 1;
-}
-
-static int
-dataiterator_match_int(Dataiterator *di)
-{
-  if ((di->flags & SEARCH_STRINGMASK) == SEARCH_REGEX)
-    return dataiterator_match_int_real(di, di->flags, &di->regex);
-  else
-    return dataiterator_match_int_real(di, di->flags, di->match);
 }
 
 int
-dataiterator_match(Dataiterator *di, int flags, const void *vmatch)
-{
-  return dataiterator_match_int_real(di, flags, vmatch);
-}
-
-static Repokey solvablekeys[RPM_RPMDBID - SOLVABLE_NAME + 1] = {
-  { SOLVABLE_NAME,        REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
-  { SOLVABLE_ARCH,        REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
-  { SOLVABLE_EVR,         REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
-  { SOLVABLE_VENDOR,      REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
-  { SOLVABLE_PROVIDES,    REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
-  { SOLVABLE_OBSOLETES,   REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
-  { SOLVABLE_CONFLICTS,   REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
-  { SOLVABLE_REQUIRES,    REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
-  { SOLVABLE_RECOMMENDS,  REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
-  { SOLVABLE_SUGGESTS,    REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
-  { SOLVABLE_SUPPLEMENTS, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
-  { SOLVABLE_ENHANCES,    REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
-  { SOLVABLE_FRESHENS,    REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
-  { RPM_RPMDBID,          REPOKEY_TYPE_U32, 0, KEY_STORAGE_SOLVABLE },
-};
-
-int
 dataiterator_step(Dataiterator *di)
 {
-restart:
-  while (1)
+  Id schema;
+
+  if (di->state == di_nextattr && di->key->storage == KEY_STORAGE_VERTICAL_OFFSET && di->vert_ddp && di->vert_storestate != di->data->storestate) {
+    unsigned int ddpoff = di->ddp - di->vert_ddp;
+    di->vert_off += ddpoff;
+    di->vert_len -= ddpoff;
+    di->ddp = di->vert_ddp = get_vertical_data(di->data, di->key, di->vert_off, di->vert_len);
+    di->vert_storestate = di->data->storestate;
+    if (!di->ddp)
+      di->state = di_nextkey;
+  }
+  for (;;)
     {
-      if (di->state)
+      switch (di->state)
        {
-         if (di->idp)
+       case di_enterrepo: di_enterrepo:
+         if (!di->repo || (di->repo->disabled && !(di->flags & SEARCH_DISABLED_REPOS)))
+           goto di_nextrepo;
+         if (!(di->flags & SEARCH_THISSOLVID))
            {
-             Id *idp = di->idp;
-             if (*idp)
+             di->solvid = di->repo->start - 1; /* reset solvid iterator */
+             goto di_nextsolvable;
+           }
+         /* FALLTHROUGH */
+
+       case di_entersolvable: di_entersolvable:
+         if (di->repodataid)
+           {
+             di->repodataid = 1;       /* reset repodata iterator */
+             if (di->solvid > 0 && !(di->flags & SEARCH_NO_STORAGE_SOLVABLE) && (!di->keyname || (di->keyname >= SOLVABLE_NAME && di->keyname <= RPM_RPMDBID)) && di->nparents - di->rootlevel == di->nkeynames)
                {
-                 di->kv.id = *idp;
-                 di->idp++;
-                 di->kv.eof = idp[1] ? 0 : 1;
-                 goto weg2;
+                 extern Repokey repo_solvablekeys[RPM_RPMDBID - SOLVABLE_NAME + 1];
+
+                 di->key = repo_solvablekeys + (di->keyname ? di->keyname - SOLVABLE_NAME : 0);
+                 di->data = 0;
+                 goto di_entersolvablekey;
                }
-             else
-               di->idp = 0;
            }
-         Solvable *s = di->repo->pool->solvables + di->solvid;
-         int state = di->state;
-         di->key = solvablekeys + state - 1;
+         /* FALLTHROUGH */
+
+       case di_enterrepodata: di_enterrepodata:
+         if (di->repodataid)
+           {
+             if (di->repodataid >= di->repo->nrepodata)
+               goto di_nextsolvable;
+             di->data = di->repo->repodata + di->repodataid;
+           }
+         if (di->repodataid && di->keyname == SOLVABLE_FILELIST && !dataiterator_filelistcheck(di))
+           goto di_nextrepodata;
+         if (!maybe_load_repodata(di->data, di->keyname))
+           goto di_nextrepodata;
+         di->dp = solvid2data(di->data, di->solvid, &schema);
+         if (!di->dp)
+           goto di_nextrepodata;
+         if (di->solvid == SOLVID_POS)
+           di->solvid = di->pool->pos.solvid;
+         /* reset key iterator */
+         di->keyp = di->data->schemadata + di->data->schemata[schema];
+         /* FALLTHROUGH */
+
+       case di_enterschema: di_enterschema:
          if (di->keyname)
-           di->state = RPM_RPMDBID;
-         else
-           di->state++;
-         if (state == 1)
+           di->dp = dataiterator_find_keyname(di, di->keyname);
+         if (!di->dp || !*di->keyp)
            {
-             di->data = 0;
-             if (di->keyname)
-               state = di->keyname - 1;
+             if (di->kv.parent)
+               goto di_leavesub;
+             goto di_nextrepodata;
            }
-         switch (state + 1)
+         /* FALLTHROUGH */
+
+       case di_enterkey: di_enterkey:
+         di->kv.entry = -1;
+         di->key = di->data->keys + *di->keyp;
+         if (!di->dp)
+           goto di_nextkey;
+         /* this is get_data() modified to store vert_ data */
+         if (di->key->storage == KEY_STORAGE_VERTICAL_OFFSET)
            {
-             case SOLVABLE_NAME:
-               if (!s->name)
-                 continue;
-               di->kv.id = s->name;
-               di->kv.eof = 1;
-               break;
-             case SOLVABLE_ARCH:
-               if (!s->arch)
-                 continue;
-               di->kv.id = s->arch;
-               di->kv.eof = 1;
-               break;
-             case SOLVABLE_EVR:
-               if (!s->evr)
-                 continue;
-               di->kv.id = s->evr;
-               di->kv.eof = 1;
-               break;
-             case SOLVABLE_VENDOR:
-               if (!s->vendor)
-                 continue;
-               di->kv.id = s->vendor;
-               di->kv.eof = 1;
-               break;
-             case SOLVABLE_PROVIDES:
-               di->idp = s->provides
-                   ? di->repo->idarraydata + s->provides : 0;
-               continue;
-             case SOLVABLE_OBSOLETES:
-               di->idp = s->obsoletes
-                   ? di->repo->idarraydata + s->obsoletes : 0;
-               continue;
-             case SOLVABLE_CONFLICTS:
-               di->idp = s->conflicts
-                   ? di->repo->idarraydata + s->conflicts : 0;
-               continue;
-             case SOLVABLE_REQUIRES:
-               di->idp = s->requires
-                   ? di->repo->idarraydata + s->requires : 0;
-               continue;
-             case SOLVABLE_RECOMMENDS:
-               di->idp = s->recommends
-                   ? di->repo->idarraydata + s->recommends : 0;
-               continue;
-             case SOLVABLE_SUPPLEMENTS:
-               di->idp = s->supplements
-                   ? di->repo->idarraydata + s->supplements : 0;
-               continue;
-             case SOLVABLE_SUGGESTS:
-               di->idp = s->suggests
-                   ? di->repo->idarraydata + s->suggests : 0;
-               continue;
-             case SOLVABLE_ENHANCES:
-               di->idp = s->enhances
-                   ? di->repo->idarraydata + s->enhances : 0;
-               continue;
-             case SOLVABLE_FRESHENS:
-               di->idp = s->freshens
-                   ? di->repo->idarraydata + s->freshens : 0;
-               continue;
-             case RPM_RPMDBID:
-               if (!di->repo->rpmdbid)
-                 continue;
-               di->kv.num = di->repo->rpmdbid[di->solvid - di->repo->start];
-               di->kv.eof = 1;
-               break;
-             default:
-               di->data = di->repo->repodata - 1;
-               di->kv.eof = 1;
-               di->state = 0;
-               continue;
+             Id off, len;
+             di->dp = data_read_id(di->dp, &off);
+             di->dp = data_read_id(di->dp, &len);
+             di->vert_ddp = di->ddp = get_vertical_data(di->data, di->key, off, len);
+             di->vert_off = off;
+             di->vert_len = len;
+             di->vert_storestate = di->data->storestate;
            }
-       }
-      else
-       {
+         else if (di->key->storage == KEY_STORAGE_INCORE)
+           {
+             di->ddp = di->dp;
+             if (di->keyp[1] && (!di->keyname || (di->flags & SEARCH_SUB) != 0))
+               di->dp = data_skip_key(di->data, di->dp, di->key);
+           }
+         else
+           di->ddp = 0;
+         if (!di->ddp)
+           goto di_nextkey;
+          if (di->key->type == REPOKEY_TYPE_DELETED)
+           goto di_nextkey;
+         if (di->key->type == REPOKEY_TYPE_FIXARRAY || di->key->type == REPOKEY_TYPE_FLEXARRAY)
+           goto di_enterarray;
+         if (di->nkeynames && di->nparents - di->rootlevel < di->nkeynames)
+           goto di_nextkey;
+         /* FALLTHROUGH */
+
+       case di_nextattr:
+          di->kv.entry++;
+         di->ddp = data_fetch(di->ddp, &di->kv, di->key);
          if (di->kv.eof)
-           di->dp = 0;
+           di->state = di_nextkey;
          else
-           di->dp = data_fetch(di->dp, &di->kv, di->key);
+           di->state = di_nextattr;
+         break;
+
+       case di_nextkey: di_nextkey:
+         if (!di->keyname && *++di->keyp)
+           goto di_enterkey;
+         if (di->kv.parent)
+           goto di_leavesub;
+         /* FALLTHROUGH */
 
-         while (!di->dp)
+       case di_nextrepodata: di_nextrepodata:
+         if (di->repodataid && ++di->repodataid < di->repo->nrepodata)
+             goto di_enterrepodata;
+         /* FALLTHROUGH */
+
+       case di_nextsolvable: di_nextsolvable:
+         if (!(di->flags & SEARCH_THISSOLVID))
            {
-             Id keyid;
-             if (di->keyname || !(keyid = *di->keyp++))
+             if (di->solvid < 0)
+               di->solvid = di->repo->start;
+             else
+               di->solvid++;
+             for (; di->solvid < di->repo->end; di->solvid++)
                {
-                 while (1)
-                   {
-                     Repo *repo = di->repo;
-                     Repodata *data = ++di->data;
-                     if (data >= repo->repodata + repo->nrepodata)
-                       {
-                         if (di->flags & __SEARCH_ONESOLVABLE)
-                           return 0;
-                         if (di->solvid >= 0)
-                           {
-                             while (++di->solvid < repo->end)
-                               if (repo->pool->solvables[di->solvid].repo == repo)
-                                 break;
-                             if (di->solvid >= repo->end)
-                               {
-                                 if (!(di->flags & SEARCH_EXTRA))
-                                   goto skiprepo;
-                                 di->solvid = -1;
-                                 if (di->solvid < -repo->nextra)
-                                   goto skiprepo;
-                               }
-                           }
-                         else
-                           {
-                             --di->solvid;
-                             if (di->solvid < -repo->nextra)
-                               {
-skiprepo:;
-                                 Pool *pool = di->repo->pool;
-                                 if (!(di->flags & SEARCH_ALL_REPOS)
-                                     || di->repo == pool->repos[pool->nrepos - 1])
-                                   return 0;
-                                 int i;
-                                 for (i = 0; i < pool->nrepos; i++)
-                                   if (di->repo == pool->repos[i])
-                                     break;
-                                 di->repo = pool->repos[i + 1];
-                                 dataiterator_init(di, di->repo, 0, di->keyname, di->match, di->flags);
-                                 continue;
-                               }
-                           }
-                         di->data = repo->repodata - 1;
-                         if (di->solvid < 0
-                             || (di->flags & SEARCH_NO_STORAGE_SOLVABLE))
-                           continue;
-                         static Id zeroid = 0;
-                         di->keyp = &zeroid;
-                         di->state = 1;
-                         goto restart;
-                       }
-                     if ((di->solvid < 0 && (-1 - di->solvid) >= data->extrastart && (-1 - di->solvid) < (data->extrastart + data->nextra))
-                         || (di->solvid >= 0 && di->solvid >= data->start && di->solvid < data->end))
-                       {
-                         dataiterator_newdata(di);
-                         if (di->nextkeydp)
-                           break;
-                       }
-                   }
+                 if (di->pool->solvables[di->solvid].repo == di->repo)
+                   goto di_entersolvable;
                }
-             else
+           }
+         /* FALLTHROUGH */
+
+       case di_nextrepo: di_nextrepo:
+         if (di->repoid > 0)
+           {
+             di->repoid++;
+             di->repodataid = 1;
+             if (di->repoid < di->pool->nrepos)
                {
-                 di->key = di->data->keys + keyid;
-                 di->dp = get_data(di->data, di->key, &di->nextkeydp);
+                 di->repo = di->pool->repos[di->repoid];
+                 goto di_enterrepo;
                }
-             di->dp = data_fetch(di->dp, &di->kv, di->key);
            }
+       /* FALLTHROUGH */
+
+       case di_bye: di_bye:
+         di->state = di_bye;
+         return 0;
+
+       case di_enterarray: di_enterarray:
+         if (di->key->name == REPOSITORY_SOLVABLES)
+           goto di_nextkey;
+         di->ddp = data_read_id(di->ddp, (Id *)&di->kv.num);
+         di->kv.eof = 0;
+         di->kv.entry = -1;
+         /* FALLTHROUGH */
+
+       case di_nextarrayelement: di_nextarrayelement:
+         di->kv.entry++;
+         if (di->kv.entry)
+           di->ddp = data_skip_schema(di->data, di->ddp, di->kv.id);
+         if (di->kv.entry == di->kv.num)
+           {
+             if (di->nkeynames && di->nparents - di->rootlevel < di->nkeynames)
+               goto di_nextkey;
+             if (!(di->flags & SEARCH_ARRAYSENTINEL))
+               goto di_nextkey;
+             di->kv.str = (char *)di->ddp;
+             di->kv.eof = 2;
+             di->state = di_nextkey;
+             break;
+           }
+         if (di->kv.entry == di->kv.num - 1)
+           di->kv.eof = 1;
+         if (di->key->type == REPOKEY_TYPE_FLEXARRAY || !di->kv.entry)
+           di->ddp = data_read_id(di->ddp, &di->kv.id);
+         di->kv.str = (char *)di->ddp;
+         if (di->nkeynames && di->nparents - di->rootlevel < di->nkeynames)
+           goto di_entersub;
+         if ((di->flags & SEARCH_SUB) != 0)
+           di->state = di_entersub;
+         else
+           di->state = di_nextarrayelement;
+         break;
+
+       case di_entersub: di_entersub:
+         if (di->nparents == sizeof(di->parents)/sizeof(*di->parents) - 1)
+           goto di_nextarrayelement;   /* sorry, full */
+         di->parents[di->nparents].kv = di->kv;
+         di->parents[di->nparents].dp = di->dp;
+         di->parents[di->nparents].keyp = di->keyp;
+         di->dp = (unsigned char *)di->kv.str;
+         di->keyp = di->data->schemadata + di->data->schemata[di->kv.id];
+         memset(&di->kv, 0, sizeof(di->kv));
+         di->kv.parent = &di->parents[di->nparents].kv;
+         di->nparents++;
+         di->keyname = di->keynames[di->nparents - di->rootlevel];
+         goto di_enterschema;
+
+       case di_leavesub: di_leavesub:
+         if (di->nparents - 1 < di->rootlevel)
+           goto di_bye;
+         di->nparents--;
+         di->dp = di->parents[di->nparents].dp;
+         di->kv = di->parents[di->nparents].kv;
+         di->keyp = di->parents[di->nparents].keyp;
+         di->key = di->data->keys + *di->keyp;
+         di->ddp = (unsigned char *)di->kv.str;
+         di->keyname = di->keynames[di->nparents - di->rootlevel];
+         goto di_nextarrayelement;
+
+        /* special solvable attr handling follows */
+
+       case di_nextsolvablekey: di_nextsolvablekey:
+         if (di->keyname || di->key->name == RPM_RPMDBID)
+           goto di_enterrepodata;
+         di->key++;
+         /* FALLTHROUGH */
+
+       case di_entersolvablekey: di_entersolvablekey:
+         di->idp = solvabledata_fetch(di->pool->solvables + di->solvid, &di->kv, di->key->name);
+         if (!di->idp || !*di->idp)
+           goto di_nextsolvablekey;
+         if (di->kv.eof)
+           {
+             /* not an array */
+             di->kv.id = *di->idp;
+             di->kv.num = *di->idp;    /* for rpmdbid */
+             di->kv.num2 = 0;          /* for rpmdbid */
+             di->kv.entry = 0;
+             di->state = di_nextsolvablekey;
+             break;
+           }
+         di->kv.entry = -1;
+         /* FALLTHROUGH */
+
+       case di_nextsolvableattr:
+         di->state = di_nextsolvableattr;
+         di->kv.id = *di->idp++;
+         di->kv.entry++;
+         if (!*di->idp)
+           {
+             di->kv.eof = 1;
+             di->state = di_nextsolvablekey;
+           }
+         break;
+
+       }
+
+      if (di->matcher.match)
+       {
+         const char *str;
+         /* simple pre-check so that we don't need to stringify */
+         if (di->keyname == SOLVABLE_FILELIST && di->key->type == REPOKEY_TYPE_DIRSTRARRAY && (di->matcher.flags & SEARCH_FILES) != 0)
+           if (!datamatcher_checkbasename(&di->matcher, di->kv.str))
+             continue;
+         if (!(str = repodata_stringify(di->pool, di->data, di->key, &di->kv, di->flags)))
+           {
+             if (di->keyname && (di->key->type == REPOKEY_TYPE_FIXARRAY || di->key->type == REPOKEY_TYPE_FLEXARRAY))
+               return 1;
+             continue;
+           }
+         if (!datamatcher_match(&di->matcher, str))
+           continue;
+       }
+      else
+       {
+         if (di->keyname == SOLVABLE_FILELIST && di->key->type == REPOKEY_TYPE_DIRSTRARRAY && (di->flags & SEARCH_FILES) != 0)
+           repodata_stringify(di->pool, di->data, di->key, &di->kv, di->flags);
        }
-weg2:
-      if (!di->match
-         || dataiterator_match_int(di))
+      /* found something! */
+      return 1;
+    }
+}
+
+void
+dataiterator_entersub(Dataiterator *di)
+{
+  if (di->state == di_nextarrayelement)
+    di->state = di_entersub;
+}
+
+void
+dataiterator_setpos(Dataiterator *di)
+{
+  if (di->kv.eof == 2)
+    {
+      pool_clear_pos(di->pool);
+      return;
+    }
+  di->pool->pos.solvid = di->solvid;
+  di->pool->pos.repo = di->repo;
+  di->pool->pos.repodataid = di->data - di->repo->repodata;
+  di->pool->pos.schema = di->kv.id;
+  di->pool->pos.dp = (unsigned char *)di->kv.str - di->data->incoredata;
+}
+
+void
+dataiterator_setpos_parent(Dataiterator *di)
+{
+  if (!di->kv.parent || di->kv.parent->eof == 2)
+    {
+      pool_clear_pos(di->pool);
+      return;
+    }
+  di->pool->pos.solvid = di->solvid;
+  di->pool->pos.repo = di->repo;
+  di->pool->pos.repodataid = di->data - di->repo->repodata;
+  di->pool->pos.schema = di->kv.parent->id;
+  di->pool->pos.dp = (unsigned char *)di->kv.parent->str - di->data->incoredata;
+}
+
+/* clones just the position, not the search keys/matcher */
+void
+dataiterator_clonepos(Dataiterator *di, Dataiterator *from)
+{
+  di->state = from->state;
+  di->flags &= ~SEARCH_THISSOLVID;
+  di->flags |= (from->flags & SEARCH_THISSOLVID);
+  di->repo = from->repo;
+  di->data = from->data;
+  di->dp = from->dp;
+  di->ddp = from->ddp;
+  di->idp = from->idp;
+  di->keyp = from->keyp;
+  di->key = from->key;
+  di->kv = from->kv;
+  di->repodataid = from->repodataid;
+  di->solvid = from->solvid;
+  di->repoid = from->repoid;
+  di->rootlevel = from->rootlevel;
+  memcpy(di->parents, from->parents, sizeof(from->parents));
+  di->nparents = from->nparents;
+  if (di->nparents)
+    {
+      int i;
+      for (i = 1; i < di->nparents; i++)
+       di->parents[i].kv.parent = &di->parents[i - 1].kv;
+      di->kv.parent = &di->parents[di->nparents - 1].kv;
+    }
+  di->dupstr = 0;
+  di->dupstrn = 0;
+  if (from->dupstr && from->dupstr == from->kv.str)
+    {
+      di->dupstrn = from->dupstrn;
+      di->dupstr = solv_memdup(from->dupstr, from->dupstrn);
+    }
+}
+
+void
+dataiterator_seek(Dataiterator *di, int whence)
+{
+  if ((whence & DI_SEEK_STAY) != 0)
+    di->rootlevel = di->nparents;
+  switch (whence & ~DI_SEEK_STAY)
+    {
+    case DI_SEEK_CHILD:
+      if (di->state != di_nextarrayelement)
        break;
+      if ((whence & DI_SEEK_STAY) != 0)
+       di->rootlevel = di->nparents + 1;       /* XXX: dangerous! */
+      di->state = di_entersub;
+      break;
+    case DI_SEEK_PARENT:
+      if (!di->nparents)
+       {
+         di->state = di_bye;
+         break;
+       }
+      di->nparents--;
+      if (di->rootlevel > di->nparents)
+       di->rootlevel = di->nparents;
+      di->dp = di->parents[di->nparents].dp;
+      di->kv = di->parents[di->nparents].kv;
+      di->keyp = di->parents[di->nparents].keyp;
+      di->key = di->data->keys + *di->keyp;
+      di->ddp = (unsigned char *)di->kv.str;
+      di->keyname = di->keynames[di->nparents - di->rootlevel];
+      di->state = di_nextarrayelement;
+      break;
+    case DI_SEEK_REWIND:
+      if (!di->nparents)
+       {
+         di->state = di_bye;
+         break;
+       }
+      di->dp = (unsigned char *)di->kv.parent->str;
+      di->keyp = di->data->schemadata + di->data->schemata[di->kv.parent->id];
+      di->state = di_enterschema;
+      break;
+    default:
+      break;
     }
-  return 1;
 }
 
 void
 dataiterator_skip_attribute(Dataiterator *di)
 {
-  if (di->state)
-    di->idp = 0;
-  /* This will make the next _step call to retrieve the next field.  */
-  di->kv.eof = 1;
+  if (di->state == di_nextsolvableattr)
+    di->state = di_nextsolvablekey;
+  else
+    di->state = di_nextkey;
 }
 
 void
 dataiterator_skip_solvable(Dataiterator *di)
 {
-  /* We're done with this field.  */
-  di->kv.eof = 1;
-  /* And with solvable data.  */
-  di->state = 0;
-  /* And with all keys for this repodata and thing. */
-  static Id zeroid = 0;
-  di->keyp = &zeroid;
-  /* And with all repodatas for this thing.  */
-  di->data = di->repo->repodata + di->repo->nrepodata - 1;
-  /* Hence the next call to _step will retrieve the next thing.  */
+  di->nparents = 0;
+  di->kv.parent = 0;
+  di->rootlevel = 0;
+  di->keyname = di->keynames[0];
+  di->state = di_nextsolvable;
 }
 
 void
 dataiterator_skip_repo(Dataiterator *di)
 {
-  dataiterator_skip_solvable(di);
-  /* We're done with all solvables and all extra things for this repo.  */
-  di->solvid = -1 - di->repo->nextra;
+  di->nparents = 0;
+  di->kv.parent = 0;
+  di->rootlevel = 0;
+  di->keyname = di->keynames[0];
+  di->state = di_nextrepo;
 }
 
 void
-dataiterator_jump_to_solvable(Dataiterator *di, Solvable *s)
+dataiterator_jump_to_solvid(Dataiterator *di, Id solvid)
 {
-  di->repo = s->repo;
-  /* Simulate us being done with the solvable before the requested one.  */
-  dataiterator_skip_solvable(di);
-  di->solvid = s - s->repo->pool->solvables;
-  di->solvid--;
+  di->nparents = 0;
+  di->kv.parent = 0;
+  di->rootlevel = 0;
+  di->keyname = di->keynames[0];
+  if (solvid == SOLVID_POS)
+    {
+      di->repo = di->pool->pos.repo;
+      if (!di->repo)
+       {
+         di->state = di_bye;
+         return;
+       }
+      di->repoid = 0;
+      di->data = di->repo->repodata + di->pool->pos.repodataid;
+      di->repodataid = 0;
+      di->solvid = solvid;
+      di->state = di_enterrepo;
+      di->flags |= SEARCH_THISSOLVID;
+      return;
+    }
+  if (solvid > 0)
+    {
+      di->repo = di->pool->solvables[solvid].repo;
+      di->repoid = 0;
+    }
+  else if (di->repoid > 0)
+    {
+      if (!di->pool->urepos)
+       {
+         di->state = di_bye;
+         return;
+       }
+      di->repoid = 1;
+      di->repo = di->pool->repos[di->repoid];
+    }
+  di->repodataid = 1;
+  di->solvid = solvid;
+  if (solvid)
+    di->flags |= SEARCH_THISSOLVID;
+  di->state = di_enterrepo;
 }
 
 void
 dataiterator_jump_to_repo(Dataiterator *di, Repo *repo)
 {
+  di->nparents = 0;
+  di->kv.parent = 0;
+  di->rootlevel = 0;
   di->repo = repo;
-  dataiterator_skip_solvable(di);
-  di->solvid = repo->start - 1;
+  di->repoid = 0;      /* 0 means stay at repo */
+  di->repodataid = 1;
+  di->solvid = 0;
+  di->flags &= ~SEARCH_THISSOLVID;
+  di->state = di_enterrepo;
+}
+
+int
+dataiterator_match(Dataiterator *di, Datamatcher *ma)
+{
+  const char *str;
+  if (!(str = repodata_stringify(di->pool, di->data, di->key, &di->kv, di->flags)))
+    return 0;
+  return ma ? datamatcher_match(ma, str) : 1;
+}
+
+void
+dataiterator_strdup(Dataiterator *di)
+{
+  int l = -1;
+
+  if (!di->kv.str || di->kv.str == di->dupstr)
+    return;
+  switch (di->key->type)
+    {
+    case_CHKSUM_TYPES:
+    case REPOKEY_TYPE_DIRSTRARRAY:
+      if (di->kv.num)  /* was it stringified into tmp space? */
+        l = strlen(di->kv.str) + 1;
+      break;
+    default:
+      break;
+    }
+  if (l < 0 && di->key->storage == KEY_STORAGE_VERTICAL_OFFSET)
+    {
+      switch (di->key->type)
+       {
+       case REPOKEY_TYPE_STR:
+       case REPOKEY_TYPE_DIRSTRARRAY:
+         l = strlen(di->kv.str) + 1;
+         break;
+       case_CHKSUM_TYPES:
+         l = solv_chksum_len(di->key->type);
+         break;
+       case REPOKEY_TYPE_BINARY:
+         l = di->kv.num;
+         break;
+       }
+    }
+  if (l >= 0)
+    {
+      if (!di->dupstrn || di->dupstrn < l)
+       {
+         di->dupstrn = l + 16;
+         di->dupstr = solv_realloc(di->dupstr, di->dupstrn);
+       }
+      if (l)
+        memcpy(di->dupstr, di->kv.str, l);
+      di->kv.str = di->dupstr;
+    }
 }
 
+/************************************************************************
+ * data modify functions
+ */
+
 /* extend repodata so that it includes solvables p */
 void
 repodata_extend(Repodata *data, Id p)
@@ -1057,10 +2041,10 @@ repodata_extend(Repodata *data, Id p)
       int new = p - data->end + 1;
       if (data->attrs)
        {
-         data->attrs = sat_extend(data->attrs, old, new, sizeof(Id), REPODATA_BLOCK);
-         memset(data->attrs + old, 0, new * sizeof(Id));
+         data->attrs = solv_extend(data->attrs, old, new, sizeof(Id *), REPODATA_BLOCK);
+         memset(data->attrs + old, 0, new * sizeof(Id *));
        }
-      data->incoreoffset = sat_extend(data->incoreoffset, old, new, sizeof(Id), REPODATA_BLOCK);
+      data->incoreoffset = solv_extend(data->incoreoffset, old, new, sizeof(Id), REPODATA_BLOCK);
       memset(data->incoreoffset + old, 0, new * sizeof(Id));
       data->end = p + 1;
     }
@@ -1070,32 +2054,49 @@ repodata_extend(Repodata *data, Id p)
       int new = data->start - p;
       if (data->attrs)
        {
-         data->attrs = sat_extend_resize(data->attrs, old + new, sizeof(Id), REPODATA_BLOCK);
-         memmove(data->attrs + new, data->attrs, old * sizeof(Id));
-         memset(data->attrs, 0, new * sizeof(Id));
+         data->attrs = solv_extend_resize(data->attrs, old + new, sizeof(Id *), REPODATA_BLOCK);
+         memmove(data->attrs + new, data->attrs, old * sizeof(Id *));
+         memset(data->attrs, 0, new * sizeof(Id *));
        }
-      data->incoreoffset = sat_extend_resize(data->incoreoffset, old + new, sizeof(Id), REPODATA_BLOCK);
+      data->incoreoffset = solv_extend_resize(data->incoreoffset, old + new, sizeof(Id), REPODATA_BLOCK);
       memmove(data->incoreoffset + new, data->incoreoffset, old * sizeof(Id));
       memset(data->incoreoffset, 0, new * sizeof(Id));
       data->start = p;
     }
 }
 
+/* shrink end of repodata */
 void
-repodata_extend_extra(Repodata *data, int nextra)
+repodata_shrink(Repodata *data, int end)
 {
-  if (nextra <= data->nextra)
+  int i;
+
+  if (data->end <= end)
     return;
-  if (data->extraattrs)
+  if (data->start >= end)
+    {
+      if (data->attrs)
+       {
+         for (i = 0; i < data->end - data->start; i++)
+           solv_free(data->attrs[i]);
+          data->attrs = solv_free(data->attrs);
+       }
+      data->incoreoffset = solv_free(data->incoreoffset);
+      data->start = data->end = 0;
+      return;
+    }
+  if (data->attrs)
     {
-      data->extraattrs = sat_extend(data->extraattrs, data->nextra, nextra - data->nextra, sizeof(Id), REPODATA_BLOCK);
-      memset(data->extraattrs + data->nextra, 0, (nextra - data->nextra) * sizeof (Id));
+      for (i = end; i < data->end; i++)
+       solv_free(data->attrs[i - data->start]);
+      data->attrs = solv_extend_resize(data->attrs, end - data->start, sizeof(Id *), REPODATA_BLOCK);
     }
-  data->extraoffset = sat_extend(data->extraoffset, data->nextra, nextra - data->nextra, sizeof(Id), REPODATA_BLOCK);
-  memset(data->extraoffset + data->nextra, 0, (nextra - data->nextra) * sizeof(Id));
-  data->nextra = nextra;
+  if (data->incoreoffset)
+    data->incoreoffset = solv_extend_resize(data->incoreoffset, end - data->start, sizeof(Id), REPODATA_BLOCK);
+  data->end = end;
 }
 
+/* extend repodata so that it includes solvables from start to start + num - 1 */
 void
 repodata_extend_block(Repodata *data, Id start, Id num)
 {
@@ -1103,7 +2104,8 @@ repodata_extend_block(Repodata *data, Id start, Id num)
     return;
   if (!data->incoreoffset)
     {
-      data->incoreoffset = sat_calloc_block(num, sizeof(Id), REPODATA_BLOCK);
+      /* this also means that data->attrs is NULL */
+      data->incoreoffset = solv_calloc_block(num, sizeof(Id), REPODATA_BLOCK);
       data->start = start;
       data->end = start + num;
       return;
@@ -1115,74 +2117,65 @@ repodata_extend_block(Repodata *data, Id start, Id num)
 
 /**********************************************************************/
 
-#define REPODATA_ATTRS_BLOCK 63
+
+#define REPODATA_ATTRS_BLOCK 31
 #define REPODATA_ATTRDATA_BLOCK 1023
 #define REPODATA_ATTRIDDATA_BLOCK 63
+#define REPODATA_ATTRNUM64DATA_BLOCK 15
+
 
-static inline Id
-get_new_struct(Repodata *data)
+Id
+repodata_new_handle(Repodata *data)
 {
-  /* Make sure to never give out struct id 0. */
-  if (!data->structs)
+  if (!data->nxattrs)
     {
-      data->structs = sat_extend(0, 0, 2, sizeof(Id *), REPODATA_BLOCK);
-      data->structs[0] = 0;
-      data->structs[1] = 0;
-      data->nstructs = 2;
-      return 1;
+      data->xattrs = solv_calloc_block(1, sizeof(Id *), REPODATA_BLOCK);
+      data->nxattrs = 2;       /* -1: SOLVID_META */
     }
-  data->structs = sat_extend(data->structs, data->nstructs, 1, sizeof(Id *), REPODATA_BLOCK);
-  data->structs[data->nstructs] = 0;
-  return data->nstructs++;
+  data->xattrs = solv_extend(data->xattrs, data->nxattrs, 1, sizeof(Id *), REPODATA_BLOCK);
+  data->xattrs[data->nxattrs] = 0;
+  return -(data->nxattrs++);
 }
 
-static Id
-repodata_get_handle_int(Repodata *data, Id entry)
+static inline Id **
+repodata_get_attrp(Repodata *data, Id handle)
 {
-  Id *ap;
-  if (!data->attrs && entry >= 0)
+  if (handle < 0)
     {
-      data->attrs = sat_calloc_block(data->end - data->start, sizeof(Id),
-                                    REPODATA_BLOCK);
+      if (handle == SOLVID_META && !data->xattrs)
+       {
+         data->xattrs = solv_calloc_block(1, sizeof(Id *), REPODATA_BLOCK);
+          data->nxattrs = 2;
+       }
+      return data->xattrs - handle;
     }
-  else if (!data->extraattrs && entry < 0)
-    data->extraattrs = sat_calloc_block(data->nextra, sizeof(Id), REPODATA_BLOCK);
-  if (entry < 0)
-    ap = &data->extraattrs[-1 - entry];
-  else
-    ap = &data->attrs[entry];
-  if (!*ap)
-    *ap = get_new_struct(data);
-  return *ap;
-}
-
-Id
-repodata_get_handle(Repodata *data, Id entry)
-{
-  return repodata_get_handle_int(data, entry);
+  if (handle < data->start || handle >= data->end)
+    repodata_extend(data, handle);
+  if (!data->attrs)
+    data->attrs = solv_calloc_block(data->end - data->start, sizeof(Id *), REPODATA_BLOCK);
+  return data->attrs + (handle - data->start);
 }
 
 static void
 repodata_insert_keyid(Repodata *data, Id handle, Id keyid, Id val, int overwrite)
 {
   Id *pp;
-  Id *ap;
-  Id sid;
+  Id *ap, **app;
   int i;
-  //sid = repodata_get_handle(data, handle);
-  sid = handle;
-  ap = data->structs[sid];
+
+  app = repodata_get_attrp(data, handle);
+  ap = *app;
   i = 0;
   if (ap)
     {
+      /* Determine equality based on the name only, allows us to change
+         type (when overwrite is set), and makes TYPE_CONSTANT work.  */
       for (pp = ap; *pp; pp += 2)
-       /* Determine equality based on the name only, allows us to change
-          type (when overwrite is set), and makes TYPE_CONSTANT work.  */
         if (data->keys[*pp].name == data->keys[keyid].name)
           break;
       if (*pp)
         {
-         if (overwrite)
+         if (overwrite || data->keys[*pp].type == REPOKEY_TYPE_DELETED)
            {
              pp[0] = keyid;
               pp[1] = val;
@@ -1191,114 +2184,103 @@ repodata_insert_keyid(Repodata *data, Id handle, Id keyid, Id val, int overwrite
         }
       i = pp - ap;
     }
-  ap = sat_extend(ap, i, 3, sizeof(Id), REPODATA_ATTRS_BLOCK);
-  data->structs[sid] = ap;
+  ap = solv_extend(ap, i, 3, sizeof(Id), REPODATA_ATTRS_BLOCK);
+  *app = ap;
   pp = ap + i;
   *pp++ = keyid;
   *pp++ = val;
   *pp = 0;
 }
-
-void
-repodata_set(Repodata *data, Id handle, Repokey *key, Id val)
-{
-  Id keyid;
-
-  /* find key in keys */
-  for (keyid = 1; keyid < data->nkeys; keyid++)
-    if (data->keys[keyid].name == key->name && data->keys[keyid].type == key->type)
-      {
-        if ((key->type == REPOKEY_TYPE_CONSTANT || key->type == REPOKEY_TYPE_CONSTANTID) && key->size != data->keys[keyid].size)
-          continue;
-        break;
-      }
-  if (keyid == data->nkeys)
-    {
-      /* allocate new key */
-      data->keys = sat_realloc2(data->keys, data->nkeys + 1, sizeof(Repokey));
-      data->keys[data->nkeys++] = *key;
-      if (data->verticaloffset)
-       {
-         data->verticaloffset = sat_realloc2(data->verticaloffset, data->nkeys, sizeof(Id));
-         data->verticaloffset[data->nkeys - 1] = 0;
-       }
-    }
-  repodata_insert_keyid(data, handle, keyid, val, 1);
+
+
+static void
+repodata_set(Repodata *data, Id solvid, Repokey *key, Id val)
+{
+  Id keyid;
+
+  keyid = repodata_key2id(data, key, 1);
+  repodata_insert_keyid(data, solvid, keyid, val, 1);
 }
 
 void
-repodata_set_id(Repodata *data, Id handle, Id keyname, Id id)
+repodata_set_id(Repodata *data, Id solvid, Id keyname, Id id)
 {
   Repokey key;
   key.name = keyname;
   key.type = REPOKEY_TYPE_ID;
   key.size = 0;
   key.storage = KEY_STORAGE_INCORE;
-  repodata_set(data, handle, &key, id);
+  repodata_set(data, solvid, &key, id);
 }
 
 void
-repodata_set_num(Repodata *data, Id handle, Id keyname, unsigned int num)
+repodata_set_num(Repodata *data, Id solvid, Id keyname, unsigned long long num)
 {
   Repokey key;
   key.name = keyname;
   key.type = REPOKEY_TYPE_NUM;
   key.size = 0;
   key.storage = KEY_STORAGE_INCORE;
-  repodata_set(data, handle, &key, (Id)num);
+  if (num >= 0x80000000)
+    {
+      data->attrnum64data = solv_extend(data->attrnum64data, data->attrnum64datalen, 1, sizeof(unsigned long long), REPODATA_ATTRNUM64DATA_BLOCK);
+      data->attrnum64data[data->attrnum64datalen] = num;
+      num = 0x80000000 | data->attrnum64datalen++;
+    }
+  repodata_set(data, solvid, &key, (Id)num);
 }
 
 void
-repodata_set_poolstr(Repodata *data, Id handle, Id keyname, const char *str)
+repodata_set_poolstr(Repodata *data, Id solvid, Id keyname, const char *str)
 {
   Repokey key;
   Id id;
   if (data->localpool)
     id = stringpool_str2id(&data->spool, str, 1);
   else
-    id = str2id(data->repo->pool, str, 1);
+    id = pool_str2id(data->repo->pool, str, 1);
   key.name = keyname;
   key.type = REPOKEY_TYPE_ID;
   key.size = 0;
   key.storage = KEY_STORAGE_INCORE;
-  repodata_set(data, handle, &key, id);
+  repodata_set(data, solvid, &key, id);
 }
 
 void
-repodata_set_constant(Repodata *data, Id handle, Id keyname, unsigned int constant)
+repodata_set_constant(Repodata *data, Id solvid, Id keyname, unsigned int constant)
 {
   Repokey key;
   key.name = keyname;
   key.type = REPOKEY_TYPE_CONSTANT;
   key.size = constant;
   key.storage = KEY_STORAGE_INCORE;
-  repodata_set(data, handle, &key, 0);
+  repodata_set(data, solvid, &key, 0);
 }
 
 void
-repodata_set_constantid(Repodata *data, Id handle, Id keyname, Id id)
+repodata_set_constantid(Repodata *data, Id solvid, Id keyname, Id id)
 {
   Repokey key;
   key.name = keyname;
   key.type = REPOKEY_TYPE_CONSTANTID;
   key.size = id;
   key.storage = KEY_STORAGE_INCORE;
-  repodata_set(data, handle, &key, 0);
+  repodata_set(data, solvid, &key, 0);
 }
 
 void
-repodata_set_void(Repodata *data, Id handle, Id keyname)
+repodata_set_void(Repodata *data, Id solvid, Id keyname)
 {
   Repokey key;
   key.name = keyname;
   key.type = REPOKEY_TYPE_VOID;
   key.size = 0;
   key.storage = KEY_STORAGE_INCORE;
-  repodata_set(data, handle, &key, 0);
+  repodata_set(data, solvid, &key, 0);
 }
 
 void
-repodata_set_str(Repodata *data, Id handle, Id keyname, const char *str)
+repodata_set_str(Repodata *data, Id solvid, Id keyname, const char *str)
 {
   Repokey key;
   int l;
@@ -1308,33 +2290,84 @@ repodata_set_str(Repodata *data, Id handle, Id keyname, const char *str)
   key.type = REPOKEY_TYPE_STR;
   key.size = 0;
   key.storage = KEY_STORAGE_INCORE;
-  data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
+  data->attrdata = solv_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
   memcpy(data->attrdata + data->attrdatalen, str, l);
-  repodata_set(data, handle, &key, data->attrdatalen);
+  repodata_set(data, solvid, &key, data->attrdatalen);
   data->attrdatalen += l;
 }
 
+void
+repodata_set_binary(Repodata *data, Id solvid, Id keyname, void *buf, int len)
+{
+  Repokey key;
+  unsigned char *dp;
+
+  if (len < 0)
+    return;
+  key.name = keyname;
+  key.type = REPOKEY_TYPE_BINARY;
+  key.size = 0;
+  key.storage = KEY_STORAGE_INCORE;
+  data->attrdata = solv_extend(data->attrdata, data->attrdatalen, len + 5, 1, REPODATA_ATTRDATA_BLOCK);
+  dp = data->attrdata + data->attrdatalen;
+  if (len >= (1 << 14))
+    {
+      if (len >= (1 << 28))
+        *dp++ = (len >> 28) | 128;
+      if (len >= (1 << 21))
+        *dp++ = (len >> 21) | 128;
+      *dp++ = (len >> 14) | 128;
+    }
+  if (len >= (1 << 7))
+    *dp++ = (len >> 7) | 128;
+  *dp++ = len & 127;
+  if (len)
+    memcpy(dp, buf, len);
+  repodata_set(data, solvid, &key, data->attrdatalen);
+  data->attrdatalen = dp + len - data->attrdata;
+}
+
+/* add an array element consisting of entrysize Ids to the repodata. modifies attriddata
+ * so that the caller can append entrysize new elements plus the termination zero there */
 static void
-repoadata_add_array(Repodata *data, Id handle, Id keyname, Id keytype, int entrysize)
+repodata_add_array(Repodata *data, Id handle, Id keyname, Id keytype, int entrysize)
 {
   int oldsize;
-  Id *ida, *pp;
+  Id *ida, *pp, **ppp;
+
+  /* check if it is the same as last time, this speeds things up a lot */
+  if (handle == data->lasthandle && data->keys[data->lastkey].name == keyname && data->keys[data->lastkey].type == keytype && data->attriddatalen == data->lastdatalen)
+    {
+      /* great! just append the new data */
+      data->attriddata = solv_extend(data->attriddata, data->attriddatalen, entrysize, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
+      data->attriddatalen--;   /* overwrite terminating 0  */
+      data->lastdatalen += entrysize;
+      return;
+    }
 
-  pp = data->structs[handle];
+  ppp = repodata_get_attrp(data, handle);
+  pp = *ppp;
   if (pp)
-    for (; *pp; pp += 2)
-      if (data->keys[*pp].name == keyname && data->keys[*pp].type == keytype)
-        break;
-  if (!pp || !*pp)
+    {
+      for (; *pp; pp += 2)
+        if (data->keys[*pp].name == keyname)
+          break;
+    }
+  if (!pp || !*pp || data->keys[*pp].type != keytype)
     {
       /* not found. allocate new key */
       Repokey key;
+      Id keyid;
       key.name = keyname;
       key.type = keytype;
       key.size = 0;
       key.storage = KEY_STORAGE_INCORE;
-      data->attriddata = sat_extend(data->attriddata, data->attriddatalen, entrysize + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
-      repodata_set(data, handle, &key, data->attriddatalen);
+      data->attriddata = solv_extend(data->attriddata, data->attriddatalen, entrysize + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
+      keyid = repodata_key2id(data, &key, 1);
+      repodata_insert_keyid(data, handle, keyid, data->attriddatalen, 1);
+      data->lasthandle = handle;
+      data->lastkey = keyid;
+      data->lastdatalen = data->attriddatalen + entrysize + 1;
       return;
     }
   oldsize = 0;
@@ -1343,134 +2376,311 @@ repoadata_add_array(Repodata *data, Id handle, Id keyname, Id keytype, int entry
   if (ida + 1 == data->attriddata + data->attriddatalen)
     {
       /* this was the last entry, just append it */
-      data->attriddata = sat_extend(data->attriddata, data->attriddatalen, entrysize, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
+      data->attriddata = solv_extend(data->attriddata, data->attriddatalen, entrysize, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
       data->attriddatalen--;   /* overwrite terminating 0  */
     }
   else
     {
       /* too bad. move to back. */
-      data->attriddata = sat_extend(data->attriddata, data->attriddatalen,  oldsize + entrysize + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
+      data->attriddata = solv_extend(data->attriddata, data->attriddatalen,  oldsize + entrysize + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
       memcpy(data->attriddata + data->attriddatalen, data->attriddata + pp[1], oldsize * sizeof(Id));
       pp[1] = data->attriddatalen;
       data->attriddatalen += oldsize;
     }
-}
-
-static inline int
-checksumtype2len(Id type)
-{
-  switch (type)
-    {
-    case REPOKEY_TYPE_MD5:
-      return SIZEOF_MD5;
-    case REPOKEY_TYPE_SHA1:
-      return SIZEOF_SHA1;
-    case REPOKEY_TYPE_SHA256:
-      return SIZEOF_SHA256;
-    default:
-      return 0;
-    }
+  data->lasthandle = handle;
+  data->lastkey = *pp;
+  data->lastdatalen = data->attriddatalen + entrysize + 1;
 }
 
 void
-repodata_set_bin_checksum(Repodata *data, Id handle, Id keyname, Id type,
+repodata_set_bin_checksum(Repodata *data, Id solvid, Id keyname, Id type,
                      const unsigned char *str)
 {
   Repokey key;
-  int l = checksumtype2len(type);
+  int l;
 
-  if (!l)
+  if (!(l = solv_chksum_len(type)))
     return;
   key.name = keyname;
   key.type = type;
   key.size = 0;
   key.storage = KEY_STORAGE_INCORE;
-  data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
+  data->attrdata = solv_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
   memcpy(data->attrdata + data->attrdatalen, str, l);
-  repodata_set(data, handle, &key, data->attrdatalen);
+  repodata_set(data, solvid, &key, data->attrdatalen);
   data->attrdatalen += l;
 }
 
-static int
-hexstr2bytes(unsigned char *buf, const char *str, int buflen)
-{
-  int i;
-  for (i = 0; i < buflen; i++)
-    {
-#define c2h(c) (((c)>='0' && (c)<='9') ? ((c)-'0')     \
-               : ((c)>='a' && (c)<='f') ? ((c)-'a'+10) \
-               : ((c)>='A' && (c)<='F') ? ((c)-'A'+10) \
-               : -1)
-      int v = c2h(*str);
-      str++;
-      if (v < 0)
-       return 0;
-      buf[i] = v;
-      v = c2h(*str);
-      str++;
-      if (v < 0)
-       return 0;
-      buf[i] = (buf[i] << 4) | v;
-#undef c2h
-    }
-  return buflen;
-}
-
 void
-repodata_set_checksum(Repodata *data, Id handle, Id keyname, Id type,
+repodata_set_checksum(Repodata *data, Id solvid, Id keyname, Id type,
                      const char *str)
 {
   unsigned char buf[64];
-  int l = checksumtype2len(type);
+  int l;
 
-  if (!l)
+  if (!(l = solv_chksum_len(type)))
     return;
-  if (hexstr2bytes(buf, str, l) != l)
-    {
-      fprintf(stderr, "Invalid hex character in '%s'\n", str);
-      return;
-    }
-  repodata_set_bin_checksum(data, handle, keyname, type, buf);
+  if (l > sizeof(buf) || solv_hex2bin(&str, buf, l) != l)
+    return;
+  repodata_set_bin_checksum(data, solvid, keyname, type, buf);
 }
 
 const char *
 repodata_chk2str(Repodata *data, Id type, const unsigned char *buf)
 {
-  int i, l;
-  char *str, *s;
+  int l;
 
-  l = checksumtype2len(type);
-  if (!l)
+  if (!(l = solv_chksum_len(type)))
     return "";
-  s = str = pool_alloctmpspace(data->repo->pool, 2 * l + 1);
-  for (i = 0; i < l; i++)
+  return pool_bin2hex(data->repo->pool, buf, l);
+}
+
+/* rpm filenames don't contain the epoch, so strip it */
+static inline const char *
+evrid2vrstr(Pool *pool, Id evrid)
+{
+  const char *p, *evr = pool_id2str(pool, evrid);
+  if (!evr)
+    return evr;
+  for (p = evr; *p >= '0' && *p <= '9'; p++)
+    ;
+  return p != evr && *p == ':' && p[1] ? p + 1 : evr;
+}
+
+static inline void
+repodata_set_poolstrn(Repodata *data, Id solvid, Id keyname, const char *str, int l)
+{
+  Id id;
+  if (data->localpool)
+    id = stringpool_strn2id(&data->spool, str, l, 1);
+  else
+    id = pool_strn2id(data->repo->pool, str, l, 1);
+  repodata_set_id(data, solvid, keyname, id);
+}
+
+static inline void
+repodata_set_strn(Repodata *data, Id solvid, Id keyname, const char *str, int l)
+{
+  if (!str[l])
+    repodata_set_str(data, solvid, keyname, str);
+  else
     {
-      unsigned char v = buf[i];
-      unsigned char w = v >> 4;
-      *s++ = w >= 10 ? w + ('a' - 10) : w + '0';
-      w = v & 15;
-      *s++ = w >= 10 ? w + ('a' - 10) : w + '0';
+      char *s = solv_strdup(str);
+      s[l] = 0;
+      repodata_set_str(data, solvid, keyname, s);
+      free(s);
     }
-  *s = 0;
-  return str;
 }
 
-Id
-repodata_globalize_id(Repodata *data, Id id)
-{ 
-  if (!data || !data->localpool)
-    return id;
-  return str2id(data->repo->pool, stringpool_id2str(&data->spool, id), 1);
+void
+repodata_set_location(Repodata *data, Id solvid, int medianr, const char *dir, const char *file)
+{
+  Pool *pool = data->repo->pool;
+  Solvable *s;
+  const char *str, *fp;
+  int l = 0;
+
+  if (medianr)
+    repodata_set_constant(data, solvid, SOLVABLE_MEDIANR, medianr);
+  if (!dir)
+    {
+      if ((dir = strrchr(file, '/')) != 0)
+       {
+          l = dir - file;
+         dir = file;
+         file = dir + l + 1;
+         if (!l)
+           l++;
+       }
+    }
+  else
+    l = strlen(dir);
+  if (l >= 2 && dir[0] == '.' && dir[1] == '/' && (l == 2 || dir[2] != '/'))
+    {
+      dir += 2;
+      l -= 2;
+    }
+  if (l == 1 && dir[0] == '.')
+    l = 0;
+  s = pool->solvables + solvid;
+  if (dir && l)
+    {
+      str = pool_id2str(pool, s->arch);
+      if (!strncmp(dir, str, l) && !str[l])
+       repodata_set_void(data, solvid, SOLVABLE_MEDIADIR);
+      else
+       repodata_set_strn(data, solvid, SOLVABLE_MEDIADIR, dir, l);
+    }
+  fp = file;
+  str = pool_id2str(pool, s->name);
+  l = strlen(str);
+  if ((!l || !strncmp(fp, str, l)) && fp[l] == '-')
+    {
+      fp += l + 1;
+      str = evrid2vrstr(pool, s->evr);
+      l = strlen(str);
+      if ((!l || !strncmp(fp, str, l)) && fp[l] == '.')
+       {
+         fp += l + 1;
+         str = pool_id2str(pool, s->arch);
+         l = strlen(str);
+         if ((!l || !strncmp(fp, str, l)) && !strcmp(fp + l, ".rpm"))
+           {
+             repodata_set_void(data, solvid, SOLVABLE_MEDIAFILE);
+             return;
+           }
+       }
+    }
+  repodata_set_str(data, solvid, SOLVABLE_MEDIAFILE, file);
+}
+
+/* XXX: medianr is currently not stored */
+void
+repodata_set_deltalocation(Repodata *data, Id handle, int medianr, const char *dir, const char *file)
+{
+  int l = 0;
+  const char *evr, *suf, *s;
+
+  if (!dir)
+    {
+      if ((dir = strrchr(file, '/')) != 0)
+       {
+          l = dir - file;
+         dir = file;
+         file = dir + l + 1;
+         if (!l)
+           l++;
+       }
+    }
+  else
+    l = strlen(dir);
+  if (l >= 2 && dir[0] == '.' && dir[1] == '/' && (l == 2 || dir[2] != '/'))
+    {
+      dir += 2;
+      l -= 2;
+    }
+  if (l == 1 && dir[0] == '.')
+    l = 0;
+  if (dir && l)
+    repodata_set_poolstrn(data, handle, DELTA_LOCATION_DIR, dir, l);
+  evr = strchr(file, '-');
+  if (evr)
+    {
+      for (s = evr - 1; s > file; s--)
+       if (*s == '-')
+         {
+           evr = s;
+           break;
+         }
+    }
+  suf = strrchr(file, '.');
+  if (suf)
+    {
+      for (s = suf - 1; s > file; s--)
+       if (*s == '.')
+         {
+           suf = s;
+           break;
+         }
+      if (!strcmp(suf, ".delta.rpm") || !strcmp(suf, ".patch.rpm"))
+       {
+         /* We accept one more item as suffix.  */
+         for (s = suf - 1; s > file; s--)
+           if (*s == '.')
+             {
+               suf = s;
+               break;
+             }
+       }
+    }
+  if (!evr)
+    suf = 0;
+  if (suf && evr && suf < evr)
+    suf = 0;
+  repodata_set_poolstrn(data, handle, DELTA_LOCATION_NAME, file, evr ? evr - file : strlen(file));
+  if (evr)
+    repodata_set_poolstrn(data, handle, DELTA_LOCATION_EVR, evr + 1, suf ? suf - evr - 1: strlen(evr + 1));
+  if (suf)
+    repodata_set_poolstr(data, handle, DELTA_LOCATION_SUFFIX, suf + 1);
+}
+
+void
+repodata_set_sourcepkg(Repodata *data, Id solvid, const char *sourcepkg)
+{
+  Pool *pool = data->repo->pool;
+  Solvable *s = pool->solvables + solvid;
+  const char *p, *sevr, *sarch, *name, *evr;
+
+  p = strrchr(sourcepkg, '.');
+  if (!p || strcmp(p, ".rpm") != 0)
+    {
+      if (*sourcepkg)
+        repodata_set_str(data, solvid, SOLVABLE_SOURCENAME, sourcepkg);
+      return;
+    }
+  p--;
+  while (p > sourcepkg && *p != '.')
+    p--;
+  if (*p != '.' || p == sourcepkg)
+    return;
+  sarch = p-- + 1;
+  while (p > sourcepkg && *p != '-')
+    p--;
+  if (*p != '-' || p == sourcepkg)
+    return;
+  p--;
+  while (p > sourcepkg && *p != '-')
+    p--;
+  if (*p != '-' || p == sourcepkg)
+    return;
+  sevr = p + 1;
+  pool = s->repo->pool;
+
+  name = pool_id2str(pool, s->name);
+  if (name && !strncmp(sourcepkg, name, sevr - sourcepkg - 1) && name[sevr - sourcepkg - 1] == 0)
+    repodata_set_void(data, solvid, SOLVABLE_SOURCENAME);
+  else
+    repodata_set_id(data, solvid, SOLVABLE_SOURCENAME, pool_strn2id(pool, sourcepkg, sevr - sourcepkg - 1, 1));
+
+  evr = evrid2vrstr(pool, s->evr);
+  if (evr && !strncmp(sevr, evr, sarch - sevr - 1) && evr[sarch - sevr - 1] == 0)
+    repodata_set_void(data, solvid, SOLVABLE_SOURCEEVR);
+  else
+    repodata_set_id(data, solvid, SOLVABLE_SOURCEEVR, pool_strn2id(pool, sevr, sarch - sevr - 1, 1));
+
+  if (!strcmp(sarch, "src.rpm"))
+    repodata_set_constantid(data, solvid, SOLVABLE_SOURCEARCH, ARCH_SRC);
+  else if (!strcmp(sarch, "nosrc.rpm"))
+    repodata_set_constantid(data, solvid, SOLVABLE_SOURCEARCH, ARCH_NOSRC);
+  else
+    repodata_set_constantid(data, solvid, SOLVABLE_SOURCEARCH, pool_strn2id(pool, sarch, strlen(sarch) - 4, 1));
 }
 
 void
-repodata_add_dirnumnum(Repodata *data, Id handle, Id keyname, Id dir, Id num, Id num2)
+repodata_set_idarray(Repodata *data, Id solvid, Id keyname, Queue *q)
 {
+  Repokey key;
+  int i;
+
+  key.name = keyname;
+  key.type = REPOKEY_TYPE_IDARRAY;
+  key.size = 0;
+  key.storage = KEY_STORAGE_INCORE;
+  repodata_set(data, solvid, &key, data->attriddatalen);
+  data->attriddata = solv_extend(data->attriddata, data->attriddatalen, q->count + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
+  for (i = 0; i < q->count; i++)
+    data->attriddata[data->attriddatalen++] = q->elements[i];
+  data->attriddata[data->attriddatalen++] = 0;
+}
 
+void
+repodata_add_dirnumnum(Repodata *data, Id solvid, Id keyname, Id dir, Id num, Id num2)
+{
+  assert(dir);
 #if 0
-fprintf(stderr, "repodata_add_dirnumnum %d %d %d %d (%d)\n", handle, dir, num, num2, data->attriddatalen);
+fprintf(stderr, "repodata_add_dirnumnum %d %d %d %d (%d)\n", solvid, dir, num, num2, data->attriddatalen);
 #endif
-  repoadata_add_array(data, handle, keyname, REPOKEY_TYPE_DIRNUMNUMARRAY, 3);
+  repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_DIRNUMNUMARRAY, 3);
   data->attriddata[data->attriddatalen++] = dir;
   data->attriddata[data->attriddatalen++] = num;
   data->attriddata[data->attriddatalen++] = num2;
@@ -1478,82 +2688,148 @@ fprintf(stderr, "repodata_add_dirnumnum %d %d %d %d (%d)\n", handle, dir, num, n
 }
 
 void
-repodata_add_dirstr(Repodata *data, Id handle, Id keyname, Id dir, const char *str)
+repodata_add_dirstr(Repodata *data, Id solvid, Id keyname, Id dir, const char *str)
 {
   Id stroff;
   int l;
 
+  assert(dir);
   l = strlen(str) + 1;
-  data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
+  data->attrdata = solv_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
   memcpy(data->attrdata + data->attrdatalen, str, l);
   stroff = data->attrdatalen;
   data->attrdatalen += l;
 
 #if 0
-fprintf(stderr, "repodata_add_dirstr %d %d %s (%d)\n", handle, dir, str,  data->attriddatalen);
+fprintf(stderr, "repodata_add_dirstr %d %d %s (%d)\n", solvid, dir, str,  data->attriddatalen);
 #endif
-  repoadata_add_array(data, handle, keyname, REPOKEY_TYPE_DIRSTRARRAY, 2);
+  repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_DIRSTRARRAY, 2);
   data->attriddata[data->attriddatalen++] = dir;
   data->attriddata[data->attriddatalen++] = stroff;
   data->attriddata[data->attriddatalen++] = 0;
 }
 
 void
-repodata_add_idarray(Repodata *data, Id handle, Id keyname, Id id)
+repodata_add_idarray(Repodata *data, Id solvid, Id keyname, Id id)
 {
 #if 0
-fprintf(stderr, "repodata_add_idarray %d %d (%d)\n", handle, id, data->attriddatalen);
+fprintf(stderr, "repodata_add_idarray %d %d (%d)\n", solvid, id, data->attriddatalen);
 #endif
-  repoadata_add_array(data, handle, keyname, REPOKEY_TYPE_IDARRAY, 1);
+  repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_IDARRAY, 1);
   data->attriddata[data->attriddatalen++] = id;
   data->attriddata[data->attriddatalen++] = 0;
 }
 
 void
-repodata_add_poolstr_array(Repodata *data, Id handle, Id keyname,
+repodata_add_poolstr_array(Repodata *data, Id solvid, Id keyname,
                           const char *str)
 {
   Id id;
   if (data->localpool)
     id = stringpool_str2id(&data->spool, str, 1);
   else
-    id = str2id(data->repo->pool, str, 1);
-  repodata_add_idarray(data, handle, keyname, id);
+    id = pool_str2id(data->repo->pool, str, 1);
+  repodata_add_idarray(data, solvid, keyname, id);
 }
 
-#if 0
 void
-repodata_open_struct(Repodata *data, Id handle, Id keyname)
+repodata_add_fixarray(Repodata *data, Id solvid, Id keyname, Id ghandle)
 {
+  repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_FIXARRAY, 1);
+  data->attriddata[data->attriddatalen++] = ghandle;
+  data->attriddata[data->attriddatalen++] = 0;
 }
 
 void
-repodata_close_struct(Repodata *data, Id entry, Id keyname)
+repodata_add_flexarray(Repodata *data, Id solvid, Id keyname, Id ghandle)
 {
+  repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_FLEXARRAY, 1);
+  data->attriddata[data->attriddatalen++] = ghandle;
+  data->attriddata[data->attriddatalen++] = 0;
+}
+
+void
+repodata_unset_uninternalized(Repodata *data, Id solvid, Id keyname)
+{
+  Id *pp, *ap, **app;
+  app = repodata_get_attrp(data, solvid);
+  ap = *app;
+  if (!ap)
+    return;
+  for (; *ap; ap += 2)
+    if (data->keys[*ap].name == keyname)
+      break;
+  if (!*ap)
+    return;
+  pp = ap;
+  ap += 2;
+  for (; *ap; ap += 2)
+    {
+      if (data->keys[*ap].name == keyname)
+       continue;
+      *pp++ = ap[0];
+      *pp++ = ap[1];
+    }
+  *pp = 0;
+}
+
+/* XXX: does not work correctly, needs fix in iterators! */
+void
+repodata_unset(Repodata *data, Id solvid, Id keyname)
+{
+  Repokey key;
+  key.name = keyname;
+  key.type = REPOKEY_TYPE_DELETED;
+  key.size = 0;
+  key.storage = KEY_STORAGE_INCORE;
+  repodata_set(data, solvid, &key, 0);
 }
-#endif
 
+/* add all (uninternalized) attrs from src to dest */
 void
 repodata_merge_attrs(Repodata *data, Id dest, Id src)
 {
   Id *keyp;
-  if (dest == src
-      || !(keyp = data->structs[src < 0
-                               ? data->extraattrs[-1 - src]
-                               : data->attrs[src]]))
+  if (dest == src || !data->attrs || !(keyp = data->attrs[src - data->start]))
     return;
-  dest = repodata_get_handle_int(data, dest);
   for (; *keyp; keyp += 2)
     repodata_insert_keyid(data, dest, keyp[0], keyp[1], 0);
 }
 
-/*********************************/
+/* add some (uninternalized) attrs from src to dest */
+void
+repodata_merge_some_attrs(Repodata *data, Id dest, Id src, Map *keyidmap, int overwrite)
+{
+  Id *keyp;
+  if (dest == src || !data->attrs || !(keyp = data->attrs[src - data->start]))
+    return;
+  for (; *keyp; keyp += 2)
+    if (!keyidmap || MAPTST(keyidmap, keyp[0]))
+      repodata_insert_keyid(data, dest, keyp[0], keyp[1], overwrite);
+}
+
+/* swap (uninternalized) attrs from src and dest */
+void
+repodata_swap_attrs(Repodata *data, Id dest, Id src)
+{
+  Id *tmpattrs;
+  if (!data->attrs || dest == src)
+    return;
+  if (dest < data->start || dest >= data->end)
+    repodata_extend(data, dest);
+  if (src < data->start || src >= data->end)
+    repodata_extend(data, src);
+  tmpattrs = data->attrs[dest - data->start];
+  data->attrs[dest - data->start] = data->attrs[src - data->start];
+  data->attrs[src - data->start] = tmpattrs;
+}
+
+
+/**********************************************************************/
 
-/* unify with repo_write! */
+/* TODO: unify with repo_write and repo_solv! */
 
 #define EXTDATA_BLOCK 1023
-#define SCHEMATA_BLOCK 31
-#define SCHEMATADATA_BLOCK 255
 
 struct extdata {
   unsigned char *buf;
@@ -1561,10 +2837,12 @@ struct extdata {
 };
 
 static void
-data_addid(struct extdata *xd, Id x)
+data_addid(struct extdata *xd, Id sx)
 {
+  unsigned int x = (unsigned int)sx;
   unsigned char *dp;
-  xd->buf = sat_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
+
+  xd->buf = solv_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
   dp = xd->buf + xd->len;
 
   if (x >= (1 << 14))
@@ -1575,124 +2853,368 @@ data_addid(struct extdata *xd, Id x)
         *dp++ = (x >> 21) | 128;
       *dp++ = (x >> 14) | 128;
     }
-  if (x >= (1 << 7))
-    *dp++ = (x >> 7) | 128;
-  *dp++ = x & 127;
-  xd->len = dp - xd->buf;
-}
-
-static void
-data_addideof(struct extdata *xd, Id x, int eof)
-{
-  if (x >= 64)
-    x = (x & 63) | ((x & ~63) << 1);
-  data_addid(xd, (eof ? x: x | 64));
-}
-
-static void
-data_addblob(struct extdata *xd, unsigned char *blob, int len)
-{
-  xd->buf = sat_extend(xd->buf, xd->len, len, 1, EXTDATA_BLOCK);
-  memcpy(xd->buf + xd->len, blob, len);
-  xd->len += len;
+  if (x >= (1 << 7))
+    *dp++ = (x >> 7) | 128;
+  *dp++ = x & 127;
+  xd->len = dp - xd->buf;
+}
+
+static void
+data_addid64(struct extdata *xd, unsigned long long x)
+{
+  if (x >= 0x100000000)
+    {
+      if ((x >> 35) != 0)
+       {
+         data_addid(xd, (Id)(x >> 35));
+         xd->buf[xd->len - 1] |= 128;
+       }
+      data_addid(xd, (Id)((unsigned int)x | 0x80000000));
+      xd->buf[xd->len - 5] = (x >> 28) | 128;
+    }
+  else
+    data_addid(xd, (Id)x);
+}
+
+static void
+data_addideof(struct extdata *xd, Id sx, int eof)
+{
+  unsigned int x = (unsigned int)sx;
+  unsigned char *dp;
+
+  xd->buf = solv_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
+  dp = xd->buf + xd->len;
+
+  if (x >= (1 << 13))
+    {
+      if (x >= (1 << 27))
+        *dp++ = (x >> 27) | 128;
+      if (x >= (1 << 20))
+        *dp++ = (x >> 20) | 128;
+      *dp++ = (x >> 13) | 128;
+    }
+  if (x >= (1 << 6))
+    *dp++ = (x >> 6) | 128;
+  *dp++ = eof ? (x & 63) : (x & 63) | 64;
+  xd->len = dp - xd->buf;
+}
+
+static void
+data_addblob(struct extdata *xd, unsigned char *blob, int len)
+{
+  xd->buf = solv_extend(xd->buf, xd->len, len, 1, EXTDATA_BLOCK);
+  memcpy(xd->buf + xd->len, blob, len);
+  xd->len += len;
+}
+
+/*********************************/
+
+/* this is to reduct memory usage when internalizing oversized repos */
+static void
+compact_attrdata(Repodata *data, int entry, int nentry)
+{
+  int i;
+  unsigned int attrdatastart = data->attrdatalen;
+  unsigned int attriddatastart = data->attriddatalen;
+  if (attrdatastart < 1024 * 1024 * 4 && attriddatastart < 1024 * 1024)
+    return;
+  for (i = entry; i < nentry; i++)
+    {
+      Id v, *attrs = data->attrs[i];
+      if (!attrs)
+       continue;
+      for (; *attrs; attrs += 2)
+       {
+         switch (data->keys[*attrs].type)
+           {
+           case REPOKEY_TYPE_STR:
+           case REPOKEY_TYPE_BINARY:
+           case_CHKSUM_TYPES:
+             if ((unsigned int)attrs[1] < attrdatastart)
+                attrdatastart = attrs[1];
+             break;
+           case REPOKEY_TYPE_DIRSTRARRAY:
+             for (v = attrs[1]; data->attriddata[v] ; v += 2)
+               if (data->attriddata[v + 1] < attrdatastart)
+                 attrdatastart = data->attriddata[v + 1];
+             /* FALLTHROUGH */
+           case REPOKEY_TYPE_IDARRAY:
+           case REPOKEY_TYPE_DIRNUMNUMARRAY:
+             if ((unsigned int)attrs[1] < attriddatastart)
+               attriddatastart = attrs[1];
+             break;
+           case REPOKEY_TYPE_FIXARRAY:
+           case REPOKEY_TYPE_FLEXARRAY:
+             return;
+           default:
+             break;
+           }
+       }
+    }
+#if 0
+  printf("compact_attrdata %d %d\n", entry, nentry);
+  printf("attrdatastart: %d\n", attrdatastart);
+  printf("attriddatastart: %d\n", attriddatastart);
+#endif
+  if (attrdatastart < 1024 * 1024 * 4 && attriddatastart < 1024 * 1024)
+    return;
+  for (i = entry; i < nentry; i++)
+    {
+      Id v, *attrs = data->attrs[i];
+      if (!attrs)
+       continue;
+      for (; *attrs; attrs += 2)
+       {
+         switch (data->keys[*attrs].type)
+           {
+           case REPOKEY_TYPE_STR:
+           case REPOKEY_TYPE_BINARY:
+           case_CHKSUM_TYPES:
+             attrs[1] -= attrdatastart;
+             break;
+           case REPOKEY_TYPE_DIRSTRARRAY:
+             for (v = attrs[1]; data->attriddata[v] ; v += 2)
+               data->attriddata[v + 1] -= attrdatastart;
+             /* FALLTHROUGH */
+           case REPOKEY_TYPE_IDARRAY:
+           case REPOKEY_TYPE_DIRNUMNUMARRAY:
+             attrs[1] -= attriddatastart;
+             break;
+           default:
+             break;
+           }
+       }
+    }
+  if (attrdatastart)
+    {
+      data->attrdatalen -= attrdatastart;
+      memmove(data->attrdata, data->attrdata + attrdatastart, data->attrdatalen);
+      data->attrdata = solv_extend_resize(data->attrdata, data->attrdatalen, 1, REPODATA_ATTRDATA_BLOCK);
+    }
+  if (attriddatastart)
+    {
+      data->attriddatalen -= attriddatastart;
+      memmove(data->attriddata, data->attriddata + attriddatastart, data->attriddatalen * sizeof(Id));
+      data->attriddata = solv_extend_resize(data->attriddata, data->attriddatalen, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
+    }
 }
 
-/*********************************/
+/* internalalize some key into incore/vincore data */
 
 static void
-addschema_prepare(Repodata *data, Id *schematacache)
-{
-  int h, len, i;
-  Id *sp;
-
-  memset(schematacache, 0, 256 * sizeof(Id));
-  for (i = 0; i < data->nschemata; i++)
+repodata_serialize_key(Repodata *data, struct extdata *newincore,
+                      struct extdata *newvincore,
+                      Id *schema,
+                      Repokey *key, Id val)
+{
+  Id *ida;
+  struct extdata *xd;
+  unsigned int oldvincorelen = 0;
+  Id schemaid, *sp;
+
+  xd = newincore;
+  if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
     {
-      for (sp = data->schemadata + data->schemata[i], h = 0; *sp; len++)
-        h = h * 7 + *sp++;
-      h &= 255;
-      schematacache[h] = i + 1;
+      xd = newvincore;
+      oldvincorelen = xd->len;
     }
-  data->schemadata = sat_extend_resize(data->schemadata, data->schemadatalen, sizeof(Id), SCHEMATADATA_BLOCK); 
-  data->schemata = sat_extend_resize(data->schemata, data->nschemata, sizeof(Id), SCHEMATA_BLOCK);
-}
-
-static Id
-addschema(Repodata *data, Id *schema, Id *schematacache)
-{
-  int h, len; 
-  Id *sp, cid; 
-
-  for (sp = schema, len = 0, h = 0; *sp; len++)
-    h = h * 7 + *sp++;
-  h &= 255; 
-  len++;
-
-  cid = schematacache[h];
-  if (cid)
-    {    
-      cid--;
-      if (!memcmp(data->schemadata + data->schemata[cid], schema, len * sizeof(Id)))
-        return cid;
-      /* cache conflict */
-      for (cid = 0; cid < data->nschemata; cid++)
-        if (!memcmp(data->schemadata + data->schemata[cid], schema, len * sizeof(Id)))
-          return cid;
+  switch (key->type)
+    {
+    case REPOKEY_TYPE_VOID:
+    case REPOKEY_TYPE_CONSTANT:
+    case REPOKEY_TYPE_CONSTANTID:
+      break;
+    case REPOKEY_TYPE_STR:
+      data_addblob(xd, data->attrdata + val, strlen((char *)(data->attrdata + val)) + 1);
+      break;
+    case REPOKEY_TYPE_MD5:
+      data_addblob(xd, data->attrdata + val, SIZEOF_MD5);
+      break;
+    case REPOKEY_TYPE_SHA1:
+      data_addblob(xd, data->attrdata + val, SIZEOF_SHA1);
+      break;
+    case REPOKEY_TYPE_SHA224:
+      data_addblob(xd, data->attrdata + val, SIZEOF_SHA224);
+      break;
+    case REPOKEY_TYPE_SHA256:
+      data_addblob(xd, data->attrdata + val, SIZEOF_SHA256);
+      break;
+    case REPOKEY_TYPE_SHA384:
+      data_addblob(xd, data->attrdata + val, SIZEOF_SHA384);
+      break;
+    case REPOKEY_TYPE_SHA512:
+      data_addblob(xd, data->attrdata + val, SIZEOF_SHA512);
+      break;
+    case REPOKEY_TYPE_NUM:
+      if (val & 0x80000000)
+       {
+         data_addid64(xd, data->attrnum64data[val ^ 0x80000000]);
+         break;
+       }
+      /* FALLTHROUGH */
+    case REPOKEY_TYPE_ID:
+    case REPOKEY_TYPE_DIR:
+      data_addid(xd, val);
+      break;
+    case REPOKEY_TYPE_BINARY:
+      {
+       Id len;
+       unsigned char *dp = data_read_id(data->attrdata + val, &len);
+       dp += (unsigned int)len;
+       data_addblob(xd, data->attrdata + val, dp - (data->attrdata + val));
+      }
+      break;
+    case REPOKEY_TYPE_IDARRAY:
+      for (ida = data->attriddata + val; *ida; ida++)
+       data_addideof(xd, ida[0], ida[1] ? 0 : 1);
+      break;
+    case REPOKEY_TYPE_DIRNUMNUMARRAY:
+      for (ida = data->attriddata + val; *ida; ida += 3)
+       {
+         data_addid(xd, ida[0]);
+         data_addid(xd, ida[1]);
+         data_addideof(xd, ida[2], ida[3] ? 0 : 1);
+       }
+      break;
+    case REPOKEY_TYPE_DIRSTRARRAY:
+      for (ida = data->attriddata + val; *ida; ida += 2)
+       {
+         data_addideof(xd, ida[0], ida[2] ? 0 : 1);
+         data_addblob(xd, data->attrdata + ida[1], strlen((char *)(data->attrdata + ida[1])) + 1);
+       }
+      break;
+    case REPOKEY_TYPE_FIXARRAY:
+      {
+       int num = 0;
+       schemaid = 0;
+       for (ida = data->attriddata + val; *ida; ida++)
+         {
+           Id *kp;
+           sp = schema;
+           kp = data->xattrs[-*ida];
+           if (!kp)
+             continue;
+           num++;
+           for (;*kp; kp += 2)
+             *sp++ = *kp;
+           *sp = 0;
+           if (!schemaid)
+             schemaid = repodata_schema2id(data, schema, 1);
+           else if (schemaid != repodata_schema2id(data, schema, 0))
+             {
+               pool_debug(data->repo->pool, SOLV_FATAL, "fixarray substructs with different schemas\n");
+               exit(1);
+             }
+         }
+       if (!num)
+         break;
+       data_addid(xd, num);
+       data_addid(xd, schemaid);
+       for (ida = data->attriddata + val; *ida; ida++)
+         {
+           Id *kp = data->xattrs[-*ida];
+           if (!kp)
+             continue;
+           for (;*kp; kp += 2)
+             repodata_serialize_key(data, newincore, newvincore, schema, data->keys + *kp, kp[1]);
+         }
+       break;
+      }
+    case REPOKEY_TYPE_FLEXARRAY:
+      {
+       int num = 0;
+       for (ida = data->attriddata + val; *ida; ida++)
+         num++;
+       data_addid(xd, num);
+       for (ida = data->attriddata + val; *ida; ida++)
+         {
+           Id *kp = data->xattrs[-*ida];
+           if (!kp)
+             {
+               data_addid(xd, 0);      /* XXX */
+               continue;
+             }
+           sp = schema;
+           for (;*kp; kp += 2)
+             *sp++ = *kp;
+           *sp = 0;
+           schemaid = repodata_schema2id(data, schema, 1);
+           data_addid(xd, schemaid);
+           kp = data->xattrs[-*ida];
+           for (;*kp; kp += 2)
+             repodata_serialize_key(data, newincore, newvincore, schema, data->keys + *kp, kp[1]);
+         }
+       break;
+      }
+    default:
+      pool_debug(data->repo->pool, SOLV_FATAL, "don't know how to handle type %d\n", key->type);
+      exit(1);
+    }
+  if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
+    {
+      /* put offset/len in incore */
+      data_addid(newincore, data->lastverticaloffset + oldvincorelen);
+      oldvincorelen = xd->len - oldvincorelen;
+      data_addid(newincore, oldvincorelen);
     }
-  /* a new one. make room. */
-  data->schemadata = sat_extend(data->schemadata, data->schemadatalen, len, sizeof(Id), SCHEMATADATA_BLOCK); 
-  data->schemata = sat_extend(data->schemata, data->nschemata, 1, sizeof(Id), SCHEMATA_BLOCK);
-  /* add schema */
-  memcpy(data->schemadata + data->schemadatalen, schema, len * sizeof(Id));
-  data->schemata[data->nschemata] = data->schemadatalen;
-  data->schemadatalen += len;
-  schematacache[h] = data->nschemata + 1;
-#if 0
-fprintf(stderr, "addschema: new schema\n");
-#endif
-  return data->nschemata++; 
 }
 
-
 void
 repodata_internalize(Repodata *data)
 {
-  Repokey *key;
-  Id id, entry, nentry, *ida;
-  Id schematacache[256];
-  Id schemaid, *schema, *sp, oldschema, *keyp, *seen;
+  Repokey *key, solvkey;
+  Id entry, nentry;
+  Id schemaid, keyid, *schema, *sp, oldschema, *keyp, *seen;
+  int schemaidx;
   unsigned char *dp, *ndp;
   int newschema, oldcount;
   struct extdata newincore;
   struct extdata newvincore;
+  Id solvkeyid;
 
-  if (!data->attrs && !data->extraattrs)
+  if (!data->attrs && !data->xattrs)
     return;
 
+#if 0
+  printf("repodata_internalize %d\n", data->repodataid);
+  printf("  attr data: %d K\n", data->attrdatalen / 1024);
+  printf("  attrid data: %d K\n", data->attriddatalen / (1024 / 4));
+#endif
   newvincore.buf = data->vincore;
   newvincore.len = data->vincorelen;
 
-  schema = sat_malloc2(data->nkeys, sizeof(Id));
-  seen = sat_malloc2(data->nkeys, sizeof(Id));
+  /* find the solvables key, create if needed */
+  memset(&solvkey, 0, sizeof(solvkey));
+  solvkey.name = REPOSITORY_SOLVABLES;
+  solvkey.type = REPOKEY_TYPE_FLEXARRAY;
+  solvkey.size = 0;
+  solvkey.storage = KEY_STORAGE_INCORE;
+  solvkeyid = repodata_key2id(data, &solvkey, data->end != data->start ? 1 : 0);
+
+  schema = solv_malloc2(data->nkeys, sizeof(Id));
+  seen = solv_malloc2(data->nkeys, sizeof(Id));
 
   /* Merge the data already existing (in data->schemata, ->incoredata and
      friends) with the new attributes in data->attrs[].  */
   nentry = data->end - data->start;
-  addschema_prepare(data, schematacache);
   memset(&newincore, 0, sizeof(newincore));
-  data_addid(&newincore, 0);
-  if (!data->attrs)
-    nentry = 0;
-  for (entry = data->extraattrs ? -data->nextra : 0; entry < nentry; entry++)
+  data_addid(&newincore, 0);   /* start data at offset 1 */
+
+  data->mainschema = 0;
+  data->mainschemaoffsets = solv_free(data->mainschemaoffsets);
+
+  /* join entry data */
+  /* we start with the meta data, entry -1 */
+  for (entry = -1; entry < nentry; entry++)
     {
-      Id sid;
       memset(seen, 0, data->nkeys * sizeof(Id));
-      sp = schema;
-      dp = entry2data(data, entry);
-      if (data->incoredata)
-        dp = data_read_id(dp, &oldschema);
-      else
-       oldschema = 0;
+      oldschema = 0;
+      dp = data->incoredata;
+      if (dp)
+       {
+         dp += entry >= 0 ? data->incoreoffset[entry] : 1;
+          dp = data_read_id(dp, &oldschema);
+       }
 #if 0
 fprintf(stderr, "oldschema %d\n", oldschema);
 fprintf(stderr, "schemata %d\n", data->schemata[oldschema]);
@@ -1701,19 +3223,33 @@ fprintf(stderr, "schemadata %p\n", data->schemadata);
       /* seen: -1: old data  0: skipped  >0: id + 1 */
       newschema = 0;
       oldcount = 0;
+      sp = schema;
       for (keyp = data->schemadata + data->schemata[oldschema]; *keyp; keyp++)
        {
          if (seen[*keyp])
            {
-             fprintf(stderr, "Inconsistent old data (key occured twice).\n");
+             pool_debug(data->repo->pool, SOLV_FATAL, "Inconsistent old data (key occured twice).\n");
              exit(1);
            }
          seen[*keyp] = -1;
          *sp++ = *keyp;
          oldcount++;
        }
-      sid = entry < 0 ? data->extraattrs[-1 - entry] : data->attrs[entry];
-      keyp = data->structs[sid];
+      if (entry >= 0)
+       keyp = data->attrs ? data->attrs[entry] : 0;
+      else
+       {
+         /* strip solvables key */
+         *sp = 0;
+         for (sp = keyp = schema; *sp; sp++)
+           if (*sp != solvkeyid)
+             *keyp++ = *sp;
+           else
+             oldcount--;
+         sp = keyp;
+         seen[solvkeyid] = 0;
+         keyp = data->xattrs ? data->xattrs[1] : 0;
+       }
       if (keyp)
         for (; *keyp; keyp += 2)
          {
@@ -1724,14 +3260,19 @@ fprintf(stderr, "schemadata %p\n", data->schemadata);
              }
            seen[*keyp] = keyp[1] + 1;
          }
-      *sp++ = 0;
+      if (entry < 0 && data->end != data->start)
+       {
+         *sp++ = solvkeyid;
+         newschema = 1;
+       }
+      *sp = 0;
       if (newschema)
         /* Ideally we'd like to sort the new schema here, to ensure
           schema equality independend of the ordering.  We can't do that
           yet.  For once see below (old ids need to come before new ids).
           An additional difficulty is that we also need to move
           the values with the keys.  */
-       schemaid = addschema(data, schema, schematacache);
+       schemaid = repodata_schema2id(data, schema, 1);
       else
        schemaid = oldschema;
 
@@ -1743,14 +3284,30 @@ fprintf(stderr, "schemadata %p\n", data->schemadata);
         (oX being the old keyids (possibly overwritten), and nX being
          the new keyids).  This rules out sorting the keyids in order
         to ensure a small schema count.  */
-      if (entry < 0)
-       data->extraoffset[-1 - entry] = newincore.len;
-      else
-       data->incoreoffset[entry] = newincore.len;
+      if (entry >= 0)
+        data->incoreoffset[entry] = newincore.len;
       data_addid(&newincore, schemaid);
-      for (keyp = data->schemadata + data->schemata[schemaid]; *keyp; keyp++)
+      if (entry == -1)
        {
-         key = data->keys + *keyp;
+         data->mainschema = schemaid;
+         data->mainschemaoffsets = solv_calloc(sp - schema, sizeof(Id));
+       }
+      /* we don't use a pointer to the schemadata here as repodata_serialize_key
+       * may call repodata_schema2id() which might realloc our schemadata */
+      for (schemaidx = data->schemata[schemaid]; (keyid = data->schemadata[schemaidx]) != 0; schemaidx++)
+       {
+         if (entry == -1)
+           data->mainschemaoffsets[schemaidx - data->schemata[schemaid]] = newincore.len;
+         if (keyid == solvkeyid)
+           {
+             /* add flexarray entry count */
+             data_addid(&newincore, data->end - data->start);
+             break;
+           }
+         key = data->keys + keyid;
+#if 0
+         fprintf(stderr, "internalize %d(%d):%s:%s\n", entry, entry + data->start, pool_id2str(data->repo->pool, key->name), pool_id2str(data->repo->pool, key->type));
+#endif
          ndp = dp;
          if (oldcount)
            {
@@ -1761,319 +3318,238 @@ fprintf(stderr, "schemadata %p\n", data->schemadata);
                  ndp = data_skip(ndp, REPOKEY_TYPE_ID);
                }
              else if (key->storage == KEY_STORAGE_INCORE)
-               ndp = data_skip(dp, key->type);
+               ndp = data_skip_key(data, dp, key);
              oldcount--;
            }
-         if (seen[*keyp] == -1)
+         if (seen[keyid] == -1)
            {
              /* If this key was an old one _and_ was not overwritten with
                 a different value copy over the old value (we skipped it
                 above).  */
              if (dp != ndp)
                data_addblob(&newincore, dp, ndp - dp);
-             seen[*keyp] = 0;
+             seen[keyid] = 0;
            }
-         else if (seen[*keyp])
+         else if (seen[keyid])
            {
-             /* Otherwise we have a new value.  Parse it into the internal
-                form.  */
-             struct extdata *xd;
-             unsigned int oldvincorelen = 0;
-
-             xd = &newincore;
-             if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
-               {
-                 xd = &newvincore;
-                 oldvincorelen = xd->len;
-               }
-             id = seen[*keyp] - 1;
-             switch (key->type)
-               {
-               case REPOKEY_TYPE_VOID:
-               case REPOKEY_TYPE_CONSTANT:
-               case REPOKEY_TYPE_CONSTANTID:
-                 break;
-               case REPOKEY_TYPE_STR:
-                 data_addblob(xd, data->attrdata + id, strlen((char *)(data->attrdata + id)) + 1);
-                 break;
-               case REPOKEY_TYPE_MD5:
-                 data_addblob(xd, data->attrdata + id, SIZEOF_MD5);
-                 break;
-               case REPOKEY_TYPE_SHA1:
-                 data_addblob(xd, data->attrdata + id, SIZEOF_SHA1);
-                 break;
-               case REPOKEY_TYPE_ID:
-               case REPOKEY_TYPE_NUM:
-               case REPOKEY_TYPE_DIR:
-                 data_addid(xd, id);
-                 break;
-               case REPOKEY_TYPE_IDARRAY:
-                 for (ida = data->attriddata + id; *ida; ida++)
-                   data_addideof(xd, ida[0], ida[1] ? 0 : 1);
-                 break;
-               case REPOKEY_TYPE_DIRNUMNUMARRAY:
-                 for (ida = data->attriddata + id; *ida; ida += 3)
-                   {
-                     data_addid(xd, ida[0]);
-                     data_addid(xd, ida[1]);
-                     data_addideof(xd, ida[2], ida[3] ? 0 : 1);
-                   }
-                 break;
-               case REPOKEY_TYPE_DIRSTRARRAY:
-                 for (ida = data->attriddata + id; *ida; ida += 2)
-                   {
-                     data_addideof(xd, ida[0], ida[2] ? 0 : 1);
-                     data_addblob(xd, data->attrdata + ida[1], strlen((char *)(data->attrdata + ida[1])) + 1);
-                   }
-                 break;
-               default:
-                 fprintf(stderr, "don't know how to handle type %d\n", key->type);
-                 exit(1);
-               }
-             if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
-               {
-                 /* put offset/len in incore */
-                 data_addid(&newincore, data->lastverticaloffset + oldvincorelen);
-                 oldvincorelen = xd->len - oldvincorelen;
-                 data_addid(&newincore, oldvincorelen);
-               }
+             /* Otherwise we have a new value.  Parse it into the internal form.  */
+             repodata_serialize_key(data, &newincore, &newvincore, schema, key, seen[keyid] - 1);
            }
          dp = ndp;
        }
-      if (data->structs[sid])
-       data->structs[sid] = sat_free(data->structs[sid]);
+      if (entry >= 0 && data->attrs)
+       {
+         if (data->attrs[entry])
+           data->attrs[entry] = solv_free(data->attrs[entry]);
+         if (entry && entry % 4096 == 0 && data->nxattrs <= 2 && entry + 64 < nentry)
+           {
+             compact_attrdata(data, entry + 1, nentry);        /* try to free some memory */
+#if 0
+             printf("  attr data: %d K\n", data->attrdatalen / 1024);
+             printf("  attrid data: %d K\n", data->attriddatalen / (1024 / 4));
+             printf("  incore data: %d K\n", newincore.len / 1024);
+             printf("  sum: %d K\n", (newincore.len + data->attrdatalen + data->attriddatalen * 4) / 1024);
+             /* malloc_stats(); */
+#endif
+           }
+       }
     }
-  for (entry = 0; entry < data->nstructs; entry++)
-    if (data->structs[entry])
-      sat_free(data->structs[entry]);
-  data->structs = sat_free(data->structs);
-  sat_free(schema);
-  sat_free(seen);
-
-  sat_free(data->incoredata);
+  /* free all xattrs */
+  for (entry = 0; entry < data->nxattrs; entry++)
+    if (data->xattrs[entry])
+      solv_free(data->xattrs[entry]);
+  data->xattrs = solv_free(data->xattrs);
+  data->nxattrs = 0;
+
+  data->lasthandle = 0;
+  data->lastkey = 0;
+  data->lastdatalen = 0;
+  solv_free(schema);
+  solv_free(seen);
+  repodata_free_schemahash(data);
+
+  solv_free(data->incoredata);
   data->incoredata = newincore.buf;
   data->incoredatalen = newincore.len;
   data->incoredatafree = 0;
-  
-  sat_free(data->vincore);
+
+  solv_free(data->vincore);
   data->vincore = newvincore.buf;
   data->vincorelen = newvincore.len;
 
-  data->attrs = sat_free(data->attrs);
-  data->extraattrs = sat_free(data->extraattrs);
-  data->attrdata = sat_free(data->attrdata);
-  data->attriddata = sat_free(data->attriddata);
+  data->attrs = solv_free(data->attrs);
+  data->attrdata = solv_free(data->attrdata);
+  data->attriddata = solv_free(data->attriddata);
+  data->attrnum64data = solv_free(data->attrnum64data);
   data->attrdatalen = 0;
   data->attriddatalen = 0;
+  data->attrnum64datalen = 0;
+#if 0
+  printf("repodata_internalize %d done\n", data->repodataid);
+  printf("  incore data: %d K\n", data->incoredatalen / 1024);
+#endif
 }
 
-Id
-repodata_str2dir(Repodata *data, const char *dir, int create)
+void
+repodata_disable_paging(Repodata *data)
 {
-  Id id, parent;
-  const char *dire;
-
-  parent = 0;
-  while (*dir == '/' && dir[1] == '/')
-    dir++;
-  if (*dir == '/' && !dir[1])
-    return 1;
-  while (*dir)
+  if (maybe_load_repodata(data, 0))
     {
-      dire = strchrnul(dir, '/');
-      if (data->localpool)
-        id = stringpool_strn2id(&data->spool, dir, dire - dir, create);
-      else
-       id = strn2id(data->repo->pool, dir, dire - dir, create);
-      if (!id)
-       return 0;
-      parent = dirpool_add_dir(&data->dirpool, parent, id, create);
-      if (!parent)
-       return 0;
-      if (!*dire)
-       break;
-      dir = dire + 1;
-      while (*dir == '/')
-       dir++;
+      repopagestore_disable_paging(&data->store);
+      data->storestate++;
     }
-  return parent;
 }
 
-const char *
-repodata_dir2str(Repodata *data, Id did, const char *suf)
+static void
+repodata_load_stub(Repodata *data)
 {
-  Pool *pool = data->repo->pool;
-  int l = 0;
-  Id parent, comp;
-  const char *comps;
-  char *p;
+  Repo *repo = data->repo;
+  Pool *pool = repo->pool;
+  int r, i;
+  struct _Pool_tmpspace oldtmpspace;
+  Datapos oldpos;
 
-  if (!did)
-    return suf ? suf : "";
-  parent = did;
-  while (parent)
-    {
-      comp = dirpool_compid(&data->dirpool, parent);
-      comps = stringpool_id2str(data->localpool ? &data->spool : &pool->ss, comp);
-      l += strlen(comps);
-      parent = dirpool_parent(&data->dirpool, parent);
-      if (parent)
-       l++;
-    }
-  if (suf)
-    l += strlen(suf) + 1;
-  p = pool_alloctmpspace(pool, l + 1) + l;
-  *p = 0;
-  if (suf)
+  if (!pool->loadcallback)
     {
-      p -= strlen(suf);
-      strcpy(p, suf);
-      *--p = '/';
+      data->state = REPODATA_ERROR;
+      return;
     }
-  parent = did;
-  while (parent)
+  data->state = REPODATA_LOADING;
+
+  /* save tmp space and pos */
+  oldtmpspace = pool->tmpspace;
+  memset(&pool->tmpspace, 0, sizeof(pool->tmpspace));
+  oldpos = pool->pos;
+
+  r = pool->loadcallback(pool, data, pool->loadcallbackdata);
+
+  /* restore tmp space and pos */
+  for (i = 0; i < POOL_TMPSPACEBUF; i++)
+    solv_free(pool->tmpspace.buf[i]);
+  pool->tmpspace = oldtmpspace;
+  if (r && oldpos.repo == repo && oldpos.repodataid == data->repodataid)
+    memset(&oldpos, 0, sizeof(oldpos));
+  pool->pos = oldpos;
+
+  data->state = r ? REPODATA_AVAILABLE : REPODATA_ERROR;
+}
+
+static inline void
+repodata_add_stubkey(Repodata *data, Id keyname, Id keytype)
+{
+  Repokey xkey;
+
+  xkey.name = keyname;
+  xkey.type = keytype;
+  xkey.storage = KEY_STORAGE_INCORE;
+  xkey.size = 0;
+  repodata_key2id(data, &xkey, 1);
+}
+
+static Repodata *
+repodata_add_stub(Repodata **datap)
+{
+  Repodata *data = *datap;
+  Repo *repo = data->repo;
+  Id repodataid = data - repo->repodata;
+  Repodata *sdata = repo_add_repodata(repo, 0);
+  data = repo->repodata + repodataid;
+  if (data->end > data->start)
+    repodata_extend_block(sdata, data->start, data->end - data->start);
+  sdata->state = REPODATA_STUB;
+  sdata->loadcallback = repodata_load_stub;
+  *datap = data;
+  return sdata;
+}
+
+Repodata *
+repodata_create_stubs(Repodata *data)
+{
+  Repo *repo = data->repo;
+  Pool *pool = repo->pool;
+  Repodata *sdata;
+  int *stubdataids;
+  Dataiterator di;
+  Id xkeyname = 0;
+  int i, cnt = 0;
+
+  dataiterator_init(&di, pool, repo, SOLVID_META, REPOSITORY_EXTERNAL, 0, 0);
+  while (dataiterator_step(&di))
+    if (di.data == data)
+      cnt++;
+  dataiterator_free(&di);
+  if (!cnt)
+    return data;
+  stubdataids = solv_calloc(cnt, sizeof(*stubdataids));
+  for (i = 0; i < cnt; i++)
     {
-      comp = dirpool_compid(&data->dirpool, parent);
-      comps = stringpool_id2str(data->localpool ? &data->spool : &pool->ss, comp);
-      l = strlen(comps);
-      p -= l;
-      strncpy(p, comps, l);
-      parent = dirpool_parent(&data->dirpool, parent);
-      if (parent)
-        *--p = '/';
+      sdata = repodata_add_stub(&data);
+      stubdataids[i] = sdata - repo->repodata;
     }
-  return p;
-}
-
-unsigned int
-repodata_compress_page(unsigned char *page, unsigned int len, unsigned char *cpage, unsigned int max)
-{
-  return compress_buf(page, len, cpage, max);
-}
-
-#define SOLV_ERROR_EOF              3
-
-static inline unsigned int
-read_u32(FILE *fp)
-{
-  int c, i;
-  unsigned int x = 0; 
-
-  for (i = 0; i < 4; i++) 
-    {    
-      c = getc(fp);
-      if (c == EOF) 
-        return 0;
-      x = (x << 8) | c; 
-    }    
-  return x;
-}
-
-#define SOLV_ERROR_EOF         3
-#define SOLV_ERROR_CORRUPT     6
-
-/* Try to either setup on-demand paging (using FP as backing
-   file), or in case that doesn't work (FP not seekable) slurps in
-   all pages and deactivates paging.  */
-void
-repodata_read_or_setup_pages(Repodata *data, unsigned int pagesz, unsigned int blobsz)
-{
-  FILE *fp = data->fp;
-  unsigned int npages;
-  unsigned int i;
-  unsigned int can_seek;
-  long cur_file_ofs;
-  unsigned char buf[BLOB_PAGESIZE];
-
-  if (pagesz != BLOB_PAGESIZE)
+  i = 0;
+  dataiterator_init(&di, pool, repo, SOLVID_META, REPOSITORY_EXTERNAL, 0, 0);
+  sdata = 0;
+  while (dataiterator_step(&di))
     {
-      /* We could handle this by slurping in everything.  */
-      data->error = SOLV_ERROR_CORRUPT;
-      return;
-    }
-  can_seek = 1;
-  if ((cur_file_ofs = ftell(fp)) < 0)
-    can_seek = 0;
-  clearerr(fp);
-  if (can_seek)
-    data->pagefd = dup(fileno(fp));
-  if (data->pagefd == -1)
-    can_seek = 0;
-
-#ifdef DEBUG_PAGING
-  fprintf (stderr, "can %sseek\n", can_seek ? "" : "NOT ");
-#endif
-  npages = (blobsz + BLOB_PAGESIZE - 1) / BLOB_PAGESIZE;
-
-  data->num_pages = npages;
-  data->pages = sat_malloc2(npages, sizeof(data->pages[0]));
-
-  /* If we can't seek on our input we have to slurp in everything.  */
-  if (!can_seek)
-    data->blob_store = sat_malloc(npages * BLOB_PAGESIZE);
-  for (i = 0; i < npages; i++)
-    {
-      unsigned int in_len = read_u32(fp);
-      unsigned int compressed = in_len & 1;
-      Attrblobpage *p = data->pages + i;
-      in_len >>= 1;
-#ifdef DEBUG_PAGING
-      fprintf (stderr, "page %d: len %d (%scompressed)\n",
-              i, in_len, compressed ? "" : "not ");
-#endif
-      if (can_seek)
-        {
-          cur_file_ofs += 4;
-         p->mapped_at = -1;
-         p->file_offset = cur_file_ofs;
-         p->file_size = in_len * 2 + compressed;
-         if (fseek(fp, in_len, SEEK_CUR) < 0)
-           {
-             perror ("fseek");
-             fprintf (stderr, "can't seek after we thought we can\n");
-             /* We can't fall back to non-seeking behaviour as we already
-                read over some data pages without storing them away.  */
-             data->error = SOLV_ERROR_EOF;
-             close(data->pagefd);
-             data->pagefd = -1;
-             return;
-           }
-         cur_file_ofs += in_len;
+      if (di.data != data)
+       continue;
+      if (di.key->name == REPOSITORY_EXTERNAL && !di.nparents)
+       {
+         dataiterator_entersub(&di);
+         sdata = repo->repodata + stubdataids[i++];
+         xkeyname = 0;
+         continue;
        }
-      else
-        {
-         unsigned int out_len;
-         void *dest = data->blob_store + i * BLOB_PAGESIZE;
-          p->mapped_at = i * BLOB_PAGESIZE;
-         p->file_offset = 0;
-         p->file_size = 0;
-         /* We can't seek, so suck everything in.  */
-         if (fread(compressed ? buf : dest, in_len, 1, fp) != 1)
-           {
-             perror("fread");
-             data->error = SOLV_ERROR_EOF;
-             return;
-           }
-         if (compressed)
+      switch (di.key->type)
+       {
+        case REPOKEY_TYPE_ID:
+         repodata_set_id(sdata, SOLVID_META, di.key->name, di.kv.id);
+         break;
+       case REPOKEY_TYPE_CONSTANTID:
+         repodata_set_constantid(sdata, SOLVID_META, di.key->name, di.kv.id);
+         break;
+       case REPOKEY_TYPE_STR:
+         repodata_set_str(sdata, SOLVID_META, di.key->name, di.kv.str);
+         break;
+       case REPOKEY_TYPE_VOID:
+         repodata_set_void(sdata, SOLVID_META, di.key->name);
+         break;
+       case REPOKEY_TYPE_NUM:
+         repodata_set_num(sdata, SOLVID_META, di.key->name, SOLV_KV_NUM64(&di.kv));
+         break;
+       case_CHKSUM_TYPES:
+         repodata_set_bin_checksum(sdata, SOLVID_META, di.key->name, di.key->type, (const unsigned char *)di.kv.str);
+         break;
+       case REPOKEY_TYPE_IDARRAY:
+         repodata_add_idarray(sdata, SOLVID_META, di.key->name, di.kv.id);
+         if (di.key->name == REPOSITORY_KEYS)
            {
-             out_len = unchecked_decompress_buf(buf, in_len, dest, BLOB_PAGESIZE);
-             if (out_len != BLOB_PAGESIZE && i < npages - 1)
-               {
-                 data->error = SOLV_ERROR_CORRUPT;
-                 return;
-               }
+             if (!xkeyname)
+               {
+                 if (!di.kv.eof)
+                   xkeyname = di.kv.id;
+               }
+             else
+               {
+                 repodata_add_stubkey(sdata, xkeyname, di.kv.id);
+                 xkeyname = 0;
+               }
            }
+         break;
+       default:
+         break;
        }
     }
+  dataiterator_free(&di);
+  for (i = 0; i < cnt; i++)
+    repodata_internalize(repo->repodata + stubdataids[i]);
+  solv_free(stubdataids);
+  return data;
 }
 
-void
-repodata_disable_paging(Repodata *data)
+unsigned int
+repodata_memused(Repodata *data)
 {
-  if (maybe_load_repodata(data, 0)
-      && data->num_pages)
-    load_page_range (data, 0, data->num_pages - 1);
+  return data->incoredatalen + data->vincorelen;
 }
-/*
-vim:cinoptions={.5s,g0,p5,t0,(0,^-0.5s,n-0.5s:tw=78:cindent:sw=4:
-*/
+