Imported Upstream version 0.6.36
[platform/upstream/libsolv.git] / src / repo_write.c
index 1e679b8..7e78af5 100644 (file)
@@ -7,12 +7,12 @@
 
 /*
  * repo_write.c
- * 
+ *
  * Write Repo data out to a file in solv format
- * 
- * See doc/README.format for a description 
+ *
+ * See doc/README.format for a description
  * of the binary file format
- * 
+ *
  */
 
 #include <sys/types.h>
@@ -22,6 +22,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <assert.h>
+#include <errno.h>
 
 #include "pool.h"
 #include "util.h"
@@ -43,9 +44,9 @@ typedef struct needid {
  * increment need Id
  * idarray: array of Ids, ID_NULL terminated
  * needid: array of Id->NeedId
- * 
+ *
  * return size of array (including trailing zero)
- * 
+ *
  */
 
 static void
@@ -92,7 +93,7 @@ incneedidarray(Pool *pool, Id *idarray, NeedId *needid)
 
 
 /*
- * 
+ *
  */
 
 static int
@@ -136,15 +137,17 @@ needid_cmp_need_s(const void *ap, const void *bp, void *dp)
  */
 
 static void
-write_u32(FILE *fp, unsigned int x)
+write_u32(Repodata *data, unsigned int x)
 {
+  FILE *fp = data->fp;
+  if (data->error)
+    return;
   if (putc(x >> 24, fp) == EOF ||
       putc(x >> 16, fp) == EOF ||
       putc(x >> 8, fp) == EOF ||
       putc(x, fp) == EOF)
     {
-      perror("write error u32");
-      exit(1);
+      data->error = pool_error(data->repo->pool, -1, "write error u32: %s", strerror(errno));
     }
 }
 
@@ -154,12 +157,13 @@ write_u32(FILE *fp, unsigned int x)
  */
 
 static void
-write_u8(FILE *fp, unsigned int x)
+write_u8(Repodata *data, unsigned int x)
 {
-  if (putc(x, fp) == EOF)
+  if (data->error)
+    return;
+  if (putc(x, data->fp) == EOF)
     {
-      perror("write error u8");
-      exit(1);
+      data->error = pool_error(data->repo->pool, -1, "write error u8: %s", strerror(errno));
     }
 }
 
@@ -168,12 +172,13 @@ write_u8(FILE *fp, unsigned int x)
  */
 
 static void
-write_blob(FILE *fp, void *data, int len)
+write_blob(Repodata *data, void *blob, int len)
 {
-  if (len && fwrite(data, len, 1, fp) != 1)
+  if (data->error)
+    return;
+  if (len && fwrite(blob, len, 1, data->fp) != 1)
     {
-      perror("write error blob");
-      exit(1);
+      data->error = pool_error(data->repo->pool, -1, "write error blob: %s", strerror(errno));
     }
 }
 
@@ -182,8 +187,11 @@ write_blob(FILE *fp, void *data, int len)
  */
 
 static void
-write_id(FILE *fp, Id x)
+write_id(Repodata *data, Id x)
 {
+  FILE *fp = data->fp;
+  if (data->error)
+    return;
   if (x >= (1 << 14))
     {
       if (x >= (1 << 28))
@@ -196,28 +204,28 @@ write_id(FILE *fp, Id x)
     putc((x >> 7) | 128, fp);
   if (putc(x & 127, fp) == EOF)
     {
-      perror("write error id");
-      exit(1);
+      data->error = pool_error(data->repo->pool, -1, "write error id: %s", strerror(errno));
     }
 }
 
 static inline void
-write_id_eof(FILE *fp, Id x, int eof)
+write_id_eof(Repodata *data, Id x, int eof)
 {
   if (x >= 64)
     x = (x & 63) | ((x & ~63) << 1);
-  write_id(fp, x | (eof ? 0 : 64));
+  write_id(data, x | (eof ? 0 : 64));
 }
 
 
 
 static inline void
-write_str(FILE *fp, const char *str)
+write_str(Repodata *data, const char *str)
 {
-  if (fputs(str, fp) == EOF || putc(0, fp) == EOF)
+  if (data->error)
+    return;
+  if (fputs(str, data->fp) == EOF || putc(0, data->fp) == EOF)
     {
-      perror("write error str");
-      exit(1);
+      data->error = pool_error(data->repo->pool, -1, "write error str: %s", strerror(errno));
     }
 }
 
@@ -226,14 +234,14 @@ write_str(FILE *fp, const char *str)
  */
 
 static void
-write_idarray(FILE *fp, Pool *pool, NeedId *needid, Id *ids)
+write_idarray(Repodata *data, Pool *pool, NeedId *needid, Id *ids)
 {
   Id id;
   if (!ids)
     return;
   if (!*ids)
     {
-      write_u8(fp, 0);
+      write_u8(data, 0);
       return;
     }
   for (;;)
@@ -245,112 +253,12 @@ write_idarray(FILE *fp, Pool *pool, NeedId *needid, Id *ids)
        id = (id & 63) | ((id & ~63) << 1);
       if (!*ids)
        {
-         write_id(fp, id);
+         write_id(data, id);
          return;
        }
-      write_id(fp, id | 64);
-    }
-}
-
-static int
-cmp_ids(const void *pa, const void *pb, void *dp)
-{
-  Id a = *(Id *)pa;
-  Id b = *(Id *)pb;
-  return a - b;
-}
-
-#if 0
-static void
-write_idarray_sort(FILE *fp, Pool *pool, NeedId *needid, Id *ids, Id marker)
-{
-  int len, i;
-  Id lids[64], *sids;
-
-  if (!ids)
-    return;
-  if (!*ids)
-    {
-      write_u8(fp, 0);
-      return;
-    }
-  for (len = 0; len < 64 && ids[len]; len++)
-    {
-      Id id = ids[len];
-      if (needid)
-        id = needid[ISRELDEP(id) ? RELOFF(id) : id].need;
-      lids[len] = id;
-    }
-  if (ids[len])
-    {
-      for (i = len + 1; ids[i]; i++)
-       ;
-      sids = solv_malloc2(i, sizeof(Id));
-      memcpy(sids, lids, 64 * sizeof(Id));
-      for (; ids[len]; len++)
-       {
-         Id id = ids[len];
-         if (needid)
-            id = needid[ISRELDEP(id) ? RELOFF(id) : id].need;
-         sids[len] = id;
-       }
-    }
-  else
-    sids = lids;
-
-  /* That bloody solvable:prereqmarker needs to stay in position :-(  */
-  if (needid)
-    marker = needid[marker].need;
-  for (i = 0; i < len; i++)
-    if (sids[i] == marker)
-      break;
-  if (i > 1)
-    solv_sort(sids, i, sizeof(Id), cmp_ids, 0);
-  if ((len - i) > 2)
-    solv_sort(sids + i + 1, len - i - 1, sizeof(Id), cmp_ids, 0);
-
-  Id id, old = 0;
-
-  /* The differencing above produces many runs of ones and twos.  I tried
-     fairly elaborate schemes to RLE those, but they give only very mediocre
-     improvements in compression, as coding the escapes costs quite some
-     space.  Even if they are coded only as bits in IDs.  The best improvement
-     was about 2.7% for the whole .solv file.  It's probably better to
-     invest some complexity into sharing idarrays, than RLEing.  */
-  for (i = 0; i < len - 1; i++)
-    {
-      id = sids[i];
-    /* Ugly PREREQ handling.  A "difference" of 0 is the prereq marker,
-       hence all real differences are offsetted by 1.  Otherwise we would
-       have to handle negative differences, which would cost code space for
-       the encoding of the sign.  We loose the exact mapping of prereq here,
-       but we know the result, so we can recover from that in the reader.  */
-      if (id == marker)
-       id = old = 0;
-      else
-       {
-          id = id - old + 1;
-         old = sids[i];
-       }
-      /* XXX If difference is zero we have multiple equal elements,
-        we might want to skip writing them out.  */
-      if (id >= 64)
-       id = (id & 63) | ((id & ~63) << 1);
-      write_id(fp, id | 64);
+      write_id(data, id | 64);
     }
-  id = sids[i];
-  if (id == marker)
-    id = 0;
-  else
-    id = id - old + 1;
-  if (id >= 64)
-    id = (id & 63) | ((id & ~63) << 1);
-  write_id(fp, id);
-  if (sids != lids)
-    solv_free(sids);
 }
-#endif
-
 
 struct extdata {
   unsigned char *buf;
@@ -389,6 +297,7 @@ struct cbdata {
   Id lastlen;
 
   int doingsolvables;  /* working on solvables data */
+  int filelistmode;
 };
 
 #define NEEDED_BLOCK 1023
@@ -397,9 +306,11 @@ struct cbdata {
 #define EXTDATA_BLOCK 4095
 
 static inline 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 = solv_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
   dp = xd->buf + xd->len;
 
@@ -418,15 +329,72 @@ data_addid(struct extdata *xd, Id x)
 }
 
 static inline void
-data_addideof(struct extdata *xd, Id x, int eof)
+data_addideof(struct extdata *xd, Id sx, int eof)
 {
-  if (x >= 64)
-    x = (x & 63) | ((x & ~63) << 1);
-  data_addid(xd, (eof ? x: x | 64));
+  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 inline int
+data_addideof_len(Id sx)
+{
+  unsigned int x = (unsigned int)sx;
+  if (x >= (1 << 13))
+    {
+      if (x >= (1 << 27))
+       return 5;
+      return x >= (1 << 20) ? 4 : 3;
+    }
+  return x >= (1 << 6) ? 2 : 1;
 }
 
 static void
-data_addidarray_sort(struct extdata *xd, Pool *pool, NeedId *needid, Id *ids, Id marker)
+data_addid64(struct extdata *xd, unsigned int x, unsigned int hx)
+{
+  if (hx)
+    {
+      if (hx > 7)
+        {
+          data_addid(xd, (Id)(hx >> 3));
+          xd->buf[xd->len - 1] |= 128;
+         hx &= 7;
+        }
+      data_addid(xd, (Id)(x | 0x80000000));
+      xd->buf[xd->len - 5] = (x >> 28) | (hx << 4) | 128;
+    }
+  else
+    data_addid(xd, (Id)x);
+}
+
+#define USE_REL_IDARRAY
+#ifdef USE_REL_IDARRAY
+
+static int
+cmp_ids(const void *pa, const void *pb, void *dp)
+{
+  Id a = *(Id *)pa;
+  Id b = *(Id *)pb;
+  return a - b;
+}
+
+static void
+data_adddepids(struct extdata *xd, Pool *pool, NeedId *needid, Id *ids, Id marker)
 {
   int len, i;
   Id lids[64], *sids;
@@ -499,22 +467,39 @@ data_addidarray_sort(struct extdata *xd, Pool *pool, NeedId *needid, Id *ids, Id
        }
       /* XXX If difference is zero we have multiple equal elements,
         we might want to skip writing them out.  */
-      if (id >= 64)
-       id = (id & 63) | ((id & ~63) << 1);
-      data_addid(xd, id | 64);
+      data_addideof(xd, id, 0);
     }
   id = sids[i];
   if (id == marker)
     id = 0;
   else
     id = id - old + 1;
-  if (id >= 64)
-    id = (id & 63) | ((id & ~63) << 1);
-  data_addid(xd, id);
+  data_addideof(xd, id, 1);
   if (sids != lids)
     solv_free(sids);
 }
 
+#else
+
+static void
+data_adddepids(struct extdata *xd, Pool *pool, NeedId *needid, Id *ids, Id marker)
+{
+  Id id;
+  if (!ids || !*ids)
+    {
+      data_addid(xd, 0);
+      return;
+    }
+  while ((id = *ids++) != 0)
+    {
+      if (needid)
+        id = needid[ISRELDEP(id) ? RELOFF(id) : id].need;
+      data_addideof(xd, id, *ids ? 0 : 1);
+    }
+}
+
+#endif
+
 static inline void
 data_addblob(struct extdata *xd, unsigned char *blob, int len)
 {
@@ -642,8 +627,8 @@ repo_write_collect_needed(struct cbdata *cbdata, Repo *repo, Repodata *data, Rep
          {
            if (cbdata->oldschema)
              {
-               fprintf(stderr, "nested structs not yet implemented\n");
-               exit(1);
+               cbdata->target->error = pool_error(cbdata->repo->pool, -1, "nested fixarray structs not yet implemented");
+               return SEARCH_NEXT_KEY;
              }
            cbdata->oldschema = cbdata->schema;
            cbdata->oldsp = cbdata->sp;
@@ -733,7 +718,7 @@ repo_write_adddata(struct cbdata *cbdata, Repodata *data, Repokey *key, KeyValue
   rm = cbdata->keymap[cbdata->keymapstart[data->repodataid] + (key - data->keys)];
   if (!rm)
     return SEARCH_NEXT_KEY;    /* we do not want this one */
-  
+
   if (cbdata->target->keys[rm].storage == KEY_STORAGE_VERTICAL_OFFSET)
     {
       xd = cbdata->extdata + rm;       /* vertical buffer */
@@ -773,9 +758,18 @@ repo_write_adddata(struct cbdata *cbdata, Repodata *data, Repokey *key, KeyValue
       case REPOKEY_TYPE_SHA1:
        data_addblob(xd, (unsigned char *)kv->str, SIZEOF_SHA1);
        break;
+      case REPOKEY_TYPE_SHA224:
+       data_addblob(xd, (unsigned char *)kv->str, SIZEOF_SHA224);
+       break;
       case REPOKEY_TYPE_SHA256:
        data_addblob(xd, (unsigned char *)kv->str, SIZEOF_SHA256);
        break;
+      case REPOKEY_TYPE_SHA384:
+       data_addblob(xd, (unsigned char *)kv->str, SIZEOF_SHA384);
+       break;
+      case REPOKEY_TYPE_SHA512:
+       data_addblob(xd, (unsigned char *)kv->str, SIZEOF_SHA512);
+       break;
       case REPOKEY_TYPE_U32:
        u32 = kv->num;
        v[0] = u32 >> 24;
@@ -785,7 +779,7 @@ repo_write_adddata(struct cbdata *cbdata, Repodata *data, Repokey *key, KeyValue
        data_addblob(xd, v, 4);
        break;
       case REPOKEY_TYPE_NUM:
-       data_addid(xd, kv->num);
+       data_addid64(xd, kv->num, kv->num2);
        break;
       case REPOKEY_TYPE_DIR:
        id = kv->id;
@@ -813,8 +807,15 @@ repo_write_adddata(struct cbdata *cbdata, Repodata *data, Repokey *key, KeyValue
        if (cbdata->owndirpool)
          id = putinowndirpool(cbdata, data, &data->dirpool, id);
        id = cbdata->dirused[id];
+       if (cbdata->filelistmode > 0)
+         {
+           xd->len += data_addideof_len(id) + strlen(kv->str) + 1;
+           break;
+         }
        data_addideof(xd, id, kv->eof);
        data_addblob(xd, (unsigned char *)kv->str, strlen(kv->str) + 1);
+       if (cbdata->filelistmode < 0)
+         return 0;
        break;
       case REPOKEY_TYPE_FIXARRAY:
        if (kv->eof == 0)
@@ -846,8 +847,8 @@ repo_write_adddata(struct cbdata *cbdata, Repodata *data, Repokey *key, KeyValue
          }
        break;
       default:
-       fprintf(stderr, "unknown type for %d: %d\n", key->name, key->type);
-       exit(1);
+       cbdata->target->error = pool_error(cbdata->repo->pool, -1, "unknown type for %d: %d\n", key->name, key->type);
+       break;
     }
   if (cbdata->target->keys[rm].storage == KEY_STORAGE_VERTICAL_OFFSET && kv->eof)
     {
@@ -886,6 +887,10 @@ traverse_dirs(Dirpool *dp, Id *dirmap, Id n, Id dir, Id *used)
       dirmap[n++] = sib;
     }
 
+  /* check if our block has some content */
+  if (parent == n)
+    return n - 1;      /* nope, drop parent id again */
+
   /* now go through all the siblings we just added and
    * do recursive calls on them */
   lastn = n;
@@ -905,7 +910,7 @@ traverse_dirs(Dirpool *dp, Id *dirmap, Id n, Id dir, Id *used)
 }
 
 static void
-write_compressed_page(FILE *fp, unsigned char *page, int len)
+write_compressed_page(Repodata *data, unsigned char *page, int len)
 {
   int clen;
   unsigned char cpage[REPOPAGE_BLOBSIZE];
@@ -913,13 +918,13 @@ write_compressed_page(FILE *fp, unsigned char *page, int len)
   clen = repopagestore_compress_page(page, len, cpage, len - 1);
   if (!clen)
     {
-      write_u32(fp, len * 2);
-      write_blob(fp, page, len);
+      write_u32(data, len * 2);
+      write_blob(data, page, len);
     }
   else
     {
-      write_u32(fp, clen * 2 + 1);
-      write_blob(fp, cpage, clen);
+      write_u32(data, clen * 2 + 1);
+      write_blob(data, cpage, clen);
     }
 }
 
@@ -931,6 +936,14 @@ static Id verticals[] = {
   SOLVABLE_EULA,
   SOLVABLE_DISKUSAGE,
   SOLVABLE_FILELIST,
+  SOLVABLE_CHECKSUM,
+  DELTA_CHECKSUM,
+  DELTA_SEQ_NUM,
+  SOLVABLE_PKGID,
+  SOLVABLE_HDRID,
+  SOLVABLE_LEADSIGID,
+  SOLVABLE_CHANGELOG_AUTHOR,
+  SOLVABLE_CHANGELOG_TEXT,
   0
 };
 
@@ -960,6 +973,47 @@ repo_write_stdkeyfilter(Repo *repo, Repokey *key, void *kfdata)
 }
 
 /*
+ * return true if the repodata contains the filelist (and just
+ * the filelist). The same code is used in the dataiterator. The way
+ * it is used is completely wrong, of course, as having the filelist
+ * key does not mean it is used for a specific solvable. Nevertheless
+ * it is better to have it than to write broken solv files.
+ */
+static inline int
+is_filelist_extension(Repodata *data)
+{
+  int j;
+  for (j = 1; j < data->nkeys; j++)
+    if (data->keys[j].name != REPOSITORY_SOLVABLES && data->keys[j].name != SOLVABLE_FILELIST)
+      return 0;
+  return 1;
+}
+
+
+static int
+write_compressed_extdata(Repodata *target, struct extdata *xd, unsigned char *vpage, int lpage)
+{
+  unsigned char *dp = xd->buf;
+  int l = xd->len;
+  while (l)
+    {
+      int ll = REPOPAGE_BLOBSIZE - lpage;
+      if (l < ll)
+       ll = l;
+      memcpy(vpage + lpage, dp, ll);
+      dp += ll;
+      lpage += ll;
+      l -= ll;
+      if (lpage == REPOPAGE_BLOBSIZE)
+       {
+         write_compressed_page(target, vpage, lpage);
+         lpage = 0;
+       }
+    }
+  return lpage;
+}
+
+/*
  * Repo
  */
 
@@ -975,10 +1029,10 @@ repo_write_stdkeyfilter(Repo *repo, Repokey *key, void *kfdata)
  * 5) write everything to disk
  */
 int
-repo_write(Repo *repo, FILE *fp, int (*keyfilter)(Repo *repo, Repokey *key, void *kfdata), void *kfdata, Id **keyarrayp)
+repo_write_filtered(Repo *repo, FILE *fp, int (*keyfilter)(Repo *repo, Repokey *key, void *kfdata), void *kfdata, Queue *keyq)
 {
   Pool *pool = repo->pool;
-  int i, j, n;
+  int i, j, n, lastfilelistn;
   Solvable *s;
   NeedId *needid;
   int nstrings, nrels;
@@ -995,7 +1049,7 @@ repo_write(Repo *repo, FILE *fp, int (*keyfilter)(Repo *repo, Repokey *key, void
   unsigned char *repodataused;
   int anyrepodataused = 0;
   int anysolvableused = 0;
-  
+
   struct cbdata cbdata;
   int clonepool;
   Repokey *key;
@@ -1015,10 +1069,6 @@ repo_write(Repo *repo, FILE *fp, int (*keyfilter)(Repo *repo, Repokey *key, void
 
   Id type_constantid = REPOKEY_TYPE_CONSTANTID;
 
-  unsigned char *prefixcomp;
-  unsigned int compsum;
-  char *old_str;
-
 
   memset(&cbdata, 0, sizeof(cbdata));
   cbdata.repo = repo;
@@ -1051,9 +1101,13 @@ repo_write(Repo *repo, FILE *fp, int (*keyfilter)(Repo *repo, Repokey *key, void
       if (i < SOLVABLE_PROVIDES)
         keyd.type = REPOKEY_TYPE_ID;
       else if (i < RPM_RPMDBID)
+#ifdef USE_REL_IDARRAY
         keyd.type = REPOKEY_TYPE_REL_IDARRAY;
+#else
+        keyd.type = REPOKEY_TYPE_IDARRAY;
+#endif
       else
-        keyd.type = REPOKEY_TYPE_U32;
+        keyd.type = REPOKEY_TYPE_NUM;
       keyd.size = 0;
       keyd.storage = KEY_STORAGE_SOLVABLE;
       if (keyfilter)
@@ -1084,6 +1138,7 @@ repo_write(Repo *repo, FILE *fp, int (*keyfilter)(Repo *repo, Repokey *key, void
   dirpool = 0;
   dirpooldata = 0;
   n = ID_NUM_INTERNAL;
+  lastfilelistn = 0;
   FOR_REPODATAS(repo, i, data)
     {
       cbdata.keymapstart[i] = n;
@@ -1174,6 +1229,20 @@ repo_write(Repo *repo, FILE *fp, int (*keyfilter)(Repo *repo, Repokey *key, void
              idused = 1;       /* dirs also use ids */
              dirused = 1;
            }
+         if (key->type == REPOKEY_TYPE_DIRSTRARRAY && key->name == SOLVABLE_FILELIST)
+           {
+             /* is this a file list extension */
+             if (is_filelist_extension(data))
+               {
+                 /* hmm, we have a file list extension. Kill filelist of other repodata.
+                  * XXX: this is wrong, as the extension does not need to cover all
+                  * solvables of the other repodata */
+                 if (lastfilelistn)
+                   cbdata.keymap[lastfilelistn] = 0;
+               }
+             else
+               lastfilelistn = n;
+           }
        }
       if (idused)
        {
@@ -1250,14 +1319,11 @@ for (i = 1; i < target.nkeys; i++)
 #endif
 
   /* copy keys if requested */
-  if (keyarrayp)
+  if (keyq)
     {
-      *keyarrayp = solv_calloc(2 * target.nkeys + 1, sizeof(Id));
+      queue_empty(keyq);
       for (i = 1; i < target.nkeys; i++)
-       {
-          (*keyarrayp)[2 * i - 2] = target.keys[i].name;
-          (*keyarrayp)[2 * i - 1] = target.keys[i].type;
-       }
+       queue_push2(keyq, target.keys[i].name, target.keys[i].type);
     }
 
   if (poolusage > 1)
@@ -1442,7 +1508,7 @@ for (i = 1; i < target.nkeys; i++)
 
   /* remove unused keys */
   keyused = solv_calloc(target.nkeys, sizeof(Id));
-  for (i = 1; i < target.schemadatalen; i++)
+  for (i = 1; i < (int)target.schemadatalen; i++)
     keyused[target.schemadata[i]] = 1;
   keyused[0] = 0;
   for (n = i = 1; i < target.nkeys; i++)
@@ -1453,24 +1519,20 @@ for (i = 1; i < target.nkeys; i++)
       if (i != n)
        {
          target.keys[n] = target.keys[i];
-         if (keyarrayp)
+         if (keyq)
            {
-             (*keyarrayp)[2 * n - 2] = (*keyarrayp)[2 * i - 2];
-             (*keyarrayp)[2 * n - 1] = (*keyarrayp)[2 * i - 1];
+             keyq->elements[2 * n - 2] = keyq->elements[2 * i - 2];
+             keyq->elements[2 * n - 1] = keyq->elements[2 * i - 1];
            }
        }
       n++;
     }
   target.nkeys = n;
-  if (keyarrayp)
-    {
-      /* terminate array */
-      (*keyarrayp)[2 * n - 2] = 0;
-      (*keyarrayp)[2 * n - 1] = 0;
-    }
+  if (keyq)
+    queue_truncate(keyq, 2 * n - 2);
 
   /* update schema data to the new key ids */
-  for (i = 1; i < target.schemadatalen; i++)
+  for (i = 1; i < (int)target.schemadatalen; i++)
     target.schemadata[i] = keyused[target.schemadata[i]];
   /* update keymap to the new key ids */
   for (i = 0; i < cbdata.nkeymap; i++)
@@ -1654,6 +1716,19 @@ fprintf(stderr, "dir %d used %d\n", i, cbdata.dirused ? cbdata.dirused[i] : 1);
     {
       data_addid(xd, repo->nsolvables);        /* FLEXARRAY nentries */
       cbdata.doingsolvables = 1;
+
+      /* check if we can do the special filelist memory optimization */
+      if (anyrepodataused)
+       {
+         for (i = 1; i < target.nkeys; i++)
+           if (target.keys[i].storage == KEY_STORAGE_VERTICAL_OFFSET)
+             cbdata.filelistmode |= cbdata.filelistmode == 0 && target.keys[i].type == REPOKEY_TYPE_DIRSTRARRAY ? 1 : 2;
+           else if (target.keys[i].type == REPOKEY_TYPE_DIRSTRARRAY)
+             cbdata.filelistmode = 2;
+         if (cbdata.filelistmode != 1)
+           cbdata.filelistmode = 0;
+       }
+
       for (i = repo->start, s = pool->solvables + i, n = 0; i < repo->end; i++, s++)
        {
          if (s->repo != repo)
@@ -1668,23 +1743,23 @@ fprintf(stderr, "dir %d used %d\n", i, cbdata.dirused ? cbdata.dirused[i] : 1);
          if (s->vendor && cbdata.keymap[SOLVABLE_VENDOR])
            data_addid(xd, needid[s->vendor].need);
          if (s->provides && cbdata.keymap[SOLVABLE_PROVIDES])
-           data_addidarray_sort(xd, pool, needid, idarraydata + s->provides, SOLVABLE_FILEMARKER);
+           data_adddepids(xd, pool, needid, idarraydata + s->provides, SOLVABLE_FILEMARKER);
          if (s->obsoletes && cbdata.keymap[SOLVABLE_OBSOLETES])
-           data_addidarray_sort(xd, pool, needid, idarraydata + s->obsoletes, 0);
+           data_adddepids(xd, pool, needid, idarraydata + s->obsoletes, 0);
          if (s->conflicts && cbdata.keymap[SOLVABLE_CONFLICTS])
-           data_addidarray_sort(xd, pool, needid, idarraydata + s->conflicts, 0);
+           data_adddepids(xd, pool, needid, idarraydata + s->conflicts, 0);
          if (s->requires && cbdata.keymap[SOLVABLE_REQUIRES])
-           data_addidarray_sort(xd, pool, needid, idarraydata + s->requires, SOLVABLE_PREREQMARKER);
+           data_adddepids(xd, pool, needid, idarraydata + s->requires, SOLVABLE_PREREQMARKER);
          if (s->recommends && cbdata.keymap[SOLVABLE_RECOMMENDS])
-           data_addidarray_sort(xd, pool, needid, idarraydata + s->recommends, 0);
+           data_adddepids(xd, pool, needid, idarraydata + s->recommends, 0);
          if (s->suggests && cbdata.keymap[SOLVABLE_SUGGESTS])
-           data_addidarray_sort(xd, pool, needid, idarraydata + s->suggests, 0);
+           data_adddepids(xd, pool, needid, idarraydata + s->suggests, 0);
          if (s->supplements && cbdata.keymap[SOLVABLE_SUPPLEMENTS])
-           data_addidarray_sort(xd, pool, needid, idarraydata + s->supplements, 0);
+           data_adddepids(xd, pool, needid, idarraydata + s->supplements, 0);
          if (s->enhances && cbdata.keymap[SOLVABLE_ENHANCES])
-           data_addidarray_sort(xd, pool, needid, idarraydata + s->enhances, 0);
+           data_adddepids(xd, pool, needid, idarraydata + s->enhances, 0);
          if (repo->rpmdbid && cbdata.keymap[RPM_RPMDBID])
-           data_addu32(xd, repo->rpmdbid[i - repo->start]);
+           data_addid(xd, repo->rpmdbid[i - repo->start]);
          if (anyrepodataused)
            {
              cbdata.vstart = -1;
@@ -1714,101 +1789,68 @@ fprintf(stderr, "dir %d used %d\n", i, cbdata.dirused ? cbdata.dirused[i] : 1);
 
 /********************************************************************/
 
+  target.fp = fp;
+
   /* write header */
 
   /* write file header */
-  write_u32(fp, 'S' << 24 | 'O' << 16 | 'L' << 8 | 'V');
-  write_u32(fp, SOLV_VERSION_8);
+  write_u32(&target, 'S' << 24 | 'O' << 16 | 'L' << 8 | 'V');
+  write_u32(&target, SOLV_VERSION_8);
 
 
   /* write counts */
-  write_u32(fp, nstrings);
-  write_u32(fp, nrels);
-  write_u32(fp, ndirmap);
-  write_u32(fp, anysolvableused ? repo->nsolvables : 0);
-  write_u32(fp, target.nkeys);
-  write_u32(fp, target.nschemata);
+  write_u32(&target, nstrings);
+  write_u32(&target, nrels);
+  write_u32(&target, ndirmap);
+  write_u32(&target, anysolvableused ? repo->nsolvables : 0);
+  write_u32(&target, target.nkeys);
+  write_u32(&target, target.nschemata);
   solv_flags = 0;
   solv_flags |= SOLV_FLAG_PREFIX_POOL;
-  write_u32(fp, solv_flags);
+  solv_flags |= SOLV_FLAG_SIZE_BYTES;
+  write_u32(&target, solv_flags);
 
-  /*
-   * calculate prefix encoding of the strings
-   */
-  prefixcomp = solv_malloc(nstrings);
-  compsum = 0;
-  old_str = "";
-  
-  prefixcomp[0] = 0;
-  for (i = 1; i < nstrings; i++)
-    {
-      char *str = spool->stringspace + spool->strings[needid[i].map];
-      int same;
-      for (same = 0; same < 255; same++)
-       if (!old_str[same] || old_str[same] != str[same])
-         break;
-      prefixcomp[i] = same;
-      compsum += same;
-      old_str = str;
-    }
-
-  /*
-   * write strings
-   */
-  write_u32(fp, sizeid);
-  /* we save compsum bytes but need 1 extra byte for every string */
-  write_u32(fp, sizeid + (nstrings ? nstrings - 1 : 0) - compsum);
-  if (sizeid + (nstrings ? nstrings - 1 : 0) != compsum)
+  if (nstrings)
     {
+      /*
+       * calculate prefix encoding of the strings
+       */
+      unsigned char *prefixcomp = solv_malloc(nstrings);
+      unsigned int compsum = 0;
+      char *old_str = "";
+
+      prefixcomp[0] = 0;
       for (i = 1; i < nstrings; i++)
        {
          char *str = spool->stringspace + spool->strings[needid[i].map];
-         write_u8(fp, prefixcomp[i]);
-         write_str(fp, str + prefixcomp[i]);
+         int same;
+         for (same = 0; same < 255; same++)
+           if (!old_str[same] || old_str[same] != str[same])
+             break;
+         prefixcomp[i] = same;
+         compsum += same;
+         old_str = str;
        }
-    }
-  solv_free(prefixcomp);
-
-#if 0
-  /* Build the prefix-encoding of the string pool.  We need to know
-     the size of that before writing it to the file, so we have to
-     build a separate buffer for that.  As it's temporarily possible
-     that this actually is an expansion we can't easily reuse the 
-     stringspace for this.  The max expansion per string is 1 byte,
-     so it will fit into sizeid+nstrings bytes.  */
-  char *prefix = solv_malloc(sizeid + nstrings);
-  char *pp = prefix;
-  char *old_str = "";
-  for (i = 1; i < nstrings; i++)
-    {
-      char *str = spool->stringspace + spool->strings[needid[i].map];
-      int same;
-      size_t len;
-      for (same = 0; same < 255; same++)
-       if (!old_str[same] || !str[same] || old_str[same] != str[same])
-         break;
-      *pp++ = same;
-      len = strlen(str + same) + 1;
-      memcpy(pp, str + same, len);
-      pp += len;
-      old_str = str;
-    }
 
-  /*
-   * write strings
-   */
-  write_u32(fp, sizeid);
-  write_u32(fp, pp - prefix);
-  if (pp != prefix)
-    {
-      if (fwrite(prefix, pp - prefix, 1, fp) != 1)
+      /*
+       * write strings
+       */
+      write_u32(&target, sizeid);
+      /* we save compsum bytes but need 1 extra byte for every string */
+      write_u32(&target, sizeid + nstrings - 1 - compsum);
+      for (i = 1; i < nstrings; i++)
        {
-         perror("write error prefix");
-         exit(1);
+         char *str = spool->stringspace + spool->strings[needid[i].map];
+         write_u8(&target, prefixcomp[i]);
+         write_str(&target, str + prefixcomp[i]);
        }
+      solv_free(prefixcomp);
+    }
+  else
+    {
+      write_u32(&target, 0);
+      write_u32(&target, 0);
     }
-  solv_free(prefix);
-#endif
 
   /*
    * write RelDeps
@@ -1816,9 +1858,9 @@ fprintf(stderr, "dir %d used %d\n", i, cbdata.dirused ? cbdata.dirused[i] : 1);
   for (i = 0; i < nrels; i++)
     {
       ran = pool->rels + (needid[reloff + i].map - reloff);
-      write_id(fp, needid[ISRELDEP(ran->name) ? RELOFF(ran->name) : ran->name].need);
-      write_id(fp, needid[ISRELDEP(ran->evr) ? RELOFF(ran->evr) : ran->evr].need);
-      write_u8(fp, ran->flags);
+      write_id(&target, needid[ISRELDEP(ran->name) ? RELOFF(ran->name) : ran->name].need);
+      write_id(&target, needid[ISRELDEP(ran->evr) ? RELOFF(ran->evr) : ran->evr].need);
+      write_u8(&target, ran->flags);
     }
 
   /*
@@ -1827,9 +1869,9 @@ fprintf(stderr, "dir %d used %d\n", i, cbdata.dirused ? cbdata.dirused[i] : 1);
   for (i = 2; i < ndirmap; i++)
     {
       if (dirmap[i] > 0)
-        write_id(fp, dirmap[i]);
+        write_id(&target, dirmap[i]);
       else
-        write_id(fp, nstrings - dirmap[i]);
+        write_id(&target, nstrings - dirmap[i]);
     }
   solv_free(dirmap);
 
@@ -1838,33 +1880,33 @@ fprintf(stderr, "dir %d used %d\n", i, cbdata.dirused ? cbdata.dirused[i] : 1);
    */
   for (i = 1; i < target.nkeys; i++)
     {
-      write_id(fp, needid[target.keys[i].name].need);
-      write_id(fp, needid[target.keys[i].type].need);
+      write_id(&target, needid[target.keys[i].name].need);
+      write_id(&target, needid[target.keys[i].type].need);
       if (target.keys[i].storage != KEY_STORAGE_VERTICAL_OFFSET)
        {
          if (target.keys[i].type == type_constantid)
-            write_id(fp, needid[target.keys[i].size].need);
+            write_id(&target, needid[target.keys[i].size].need);
          else
-            write_id(fp, target.keys[i].size);
+            write_id(&target, target.keys[i].size);
        }
       else
-        write_id(fp, cbdata.extdata[i].len);
-      write_id(fp, target.keys[i].storage);
+        write_id(&target, cbdata.extdata[i].len);
+      write_id(&target, target.keys[i].storage);
     }
 
   /*
    * write schemata
    */
-  write_id(fp, target.schemadatalen);  /* XXX -1? */
+  write_id(&target, target.schemadatalen);     /* XXX -1? */
   for (i = 1; i < target.nschemata; i++)
-    write_idarray(fp, pool, 0, repodata_id2schema(&target, i));
+    write_idarray(&target, pool, 0, repodata_id2schema(&target, i));
 
 /********************************************************************/
 
-  write_id(fp, cbdata.maxdata);
-  write_id(fp, cbdata.extdata[0].len);
+  write_id(&target, cbdata.maxdata);
+  write_id(&target, cbdata.extdata[0].len);
   if (cbdata.extdata[0].len)
-    write_blob(fp, cbdata.extdata[0].buf, cbdata.extdata[0].len);
+    write_blob(&target, cbdata.extdata[0].buf, cbdata.extdata[0].len);
   solv_free(cbdata.extdata[0].buf);
 
   /* do we have vertical data? */
@@ -1874,40 +1916,56 @@ fprintf(stderr, "dir %d used %d\n", i, cbdata.dirused ? cbdata.dirused[i] : 1);
   if (i < target.nkeys)
     {
       /* yes, write it in pages */
-      unsigned char *dp, vpage[REPOPAGE_BLOBSIZE];
-      int l, ll, lpage = 0;
+      unsigned char vpage[REPOPAGE_BLOBSIZE];
+      int lpage = 0;
 
-      write_u32(fp, REPOPAGE_BLOBSIZE);
+      write_u32(&target, REPOPAGE_BLOBSIZE);
       for (i = 1; i < target.nkeys; i++)
+       if (cbdata.extdata[i].len)
+         {
+           if (cbdata.filelistmode)
+             break;
+           lpage = write_compressed_extdata(&target, cbdata.extdata + i, vpage, lpage);
+         }
+      if (cbdata.filelistmode && i < target.nkeys)
        {
-         if (!cbdata.extdata[i].len)
-           continue;
-         l = cbdata.extdata[i].len;
-         dp = cbdata.extdata[i].buf;
-         while (l)
+         /* ok, just this single extdata, which is a filelist */
+         xd = cbdata.extdata + i;
+         xd->len = 0;
+         cbdata.filelistmode = -1;
+         for (j = 0; j < cbdata.nkeymap; j++)
+           if (cbdata.keymap[j] != i)
+             cbdata.keymap[j] = 0;
+         for (i = repo->start, s = pool->solvables + i; i < repo->end; i++, s++)
            {
-             ll = REPOPAGE_BLOBSIZE - lpage;
-             if (l < ll)
-               ll = l;
-             memcpy(vpage + lpage, dp, ll);
-             dp += ll;
-             lpage += ll;
-             l -= ll;
-             if (lpage == REPOPAGE_BLOBSIZE)
+             if (s->repo != repo)
+               continue;
+             FOR_REPODATAS(repo, j, data)
+               {
+                 if (!repodataused[j])
+                   continue;
+                 if (i < data->start || i >= data->end)
+                   continue;
+                 repodata_search(data, i, 0, SEARCH_SUB|SEARCH_ARRAYSENTINEL, repo_write_cb_adddata, &cbdata);
+               }
+             if (xd->len > 1024 * 1024)
                {
-                 write_compressed_page(fp, vpage, lpage);
-                 lpage = 0;
+                 lpage = write_compressed_extdata(&target, xd, vpage, lpage);
+                 xd->len = 0;
                }
            }
+         if (xd->len)
+           lpage = write_compressed_extdata(&target, xd, vpage, lpage);
        }
       if (lpage)
-       write_compressed_page(fp, vpage, lpage);
+       write_compressed_page(&target, vpage, lpage);
     }
 
   for (i = 1; i < target.nkeys; i++)
     solv_free(cbdata.extdata[i].buf);
   solv_free(cbdata.extdata);
 
+  target.fp = 0;
   repodata_freedata(&target);
 
   solv_free(needid);
@@ -1918,7 +1976,7 @@ fprintf(stderr, "dir %d used %d\n", i, cbdata.dirused ? cbdata.dirused[i] : 1);
   solv_free(cbdata.keymapstart);
   solv_free(cbdata.dirused);
   solv_free(repodataused);
-  return 0;
+  return target.error;
 }
 
 struct repodata_write_data {
@@ -1943,12 +2001,24 @@ repodata_write_keyfilter(Repo *repo, Repokey *key, void *kfdata)
 }
 
 int
-repodata_write(Repodata *data, FILE *fp, int (*keyfilter)(Repo *repo, Repokey *key, void *kfdata), void *kfdata)
+repodata_write_filtered(Repodata *data, FILE *fp, int (*keyfilter)(Repo *repo, Repokey *key, void *kfdata), void *kfdata, Queue *keyq)
 {
   struct repodata_write_data wd;
 
   wd.keyfilter = keyfilter;
   wd.kfdata = kfdata;
   wd.repodataid = data->repodataid;
-  return repo_write(data->repo, fp, repodata_write_keyfilter, &wd, 0);
+  return repo_write_filtered(data->repo, fp, repodata_write_keyfilter, &wd, keyq);
+}
+
+int
+repodata_write(Repodata *data, FILE *fp)
+{
+  return repodata_write_filtered(data, fp, repo_write_stdkeyfilter, 0, 0);
+}
+
+int
+repo_write(Repo *repo, FILE *fp)
+{
+  return repo_write_filtered(repo, fp, repo_write_stdkeyfilter, 0, 0);
 }