4c69fabd1ff5691a6f496d253ae4d8bc569f56f5
[platform/upstream/libsolv.git] / tools / repo_write.c
1 /*
2  * Copyright (c) 2007, Novell Inc.
3  *
4  * This program is licensed under the BSD license, read LICENSE.BSD
5  * for further information
6  */
7
8 /*
9  * repo_write.c
10  * 
11  * Write Repo data out to binary file
12  * 
13  * See doc/README.format for a description 
14  * of the binary file format
15  * 
16  */
17
18 #include <sys/types.h>
19 #include <limits.h>
20 #include <fcntl.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <assert.h>
25
26 #include "pool.h"
27 #include "util.h"
28 #include "repo_write.h"
29
30 /* #define IGNORE_NEED_FREQUENCY */
31
32 /*------------------------------------------------------------------*/
33 /* Id map optimizations */
34
35 typedef struct needid {
36   Id need;
37   Id map;
38 } NeedId;
39
40
41 #define RELOFF(id) (pool->ss.nstrings + GETRELID(id))
42
43 /*
44  * increment need Id
45  * idarray: array of Ids, ID_NULL terminated
46  * needid: array of Id->NeedId
47  * 
48  * return count
49  * 
50  */
51
52 static int
53 incneedid(Pool *pool, Id *idarray, NeedId *needid)
54 {
55   if (!idarray)
56     return 0;
57
58   Id id;
59   int n = 0;
60
61   while ((id = *idarray++) != 0)
62     {
63       n++;
64       while (ISRELDEP(id))
65         {
66           Reldep *rd = GETRELDEP(pool, id);
67           needid[RELOFF(id)].need++;
68           if (ISRELDEP(rd->evr))
69             {
70               Id ida[2];
71               ida[0] = rd->evr;
72               ida[1] = 0;
73               incneedid(pool, ida, needid);
74             }
75           else
76             needid[rd->evr].need++;
77           id = rd->name;
78         }
79       needid[id].need++;
80     }
81   return n + 1;
82 }
83
84
85 /*
86  * 
87  */
88
89 static int
90 needid_cmp_need(const void *ap, const void *bp)
91 {
92   const NeedId *a = ap;
93   const NeedId *b = bp;
94   int r;
95   r = b->need - a->need;
96   if (r)
97     return r;
98   return a->map - b->map;
99 }
100
101 static Pool *cmp_pool;
102 static int
103 needid_cmp_need_s(const void *ap, const void *bp)
104 {
105   const NeedId *a = ap;
106   const NeedId *b = bp;
107   if (a == b)
108     return 0;
109 #ifdef IGNORE_NEED_FREQUENCY
110   if (a->need == 0)
111     return 1;
112   else if (b->need == 0)
113     return -1;
114 #else
115   int r;
116   r = b->need - a->need;
117   if (r)
118     return r;
119 #endif
120   char *as = cmp_pool->ss.stringspace + cmp_pool->ss.strings[a->map];
121   char *bs = cmp_pool->ss.stringspace + cmp_pool->ss.strings[b->map];
122   size_t alen = strlen (as);
123   size_t blen = strlen (bs);
124   return memcmp (as, bs, alen < blen ? alen + 1 : blen + 1);
125 }
126
127
128 /*------------------------------------------------------------------*/
129 /* output routines */
130
131 /*
132  * unsigned 32-bit
133  */
134
135 static void
136 write_u32(FILE *fp, unsigned int x)
137 {
138   if (putc(x >> 24, fp) == EOF ||
139       putc(x >> 16, fp) == EOF ||
140       putc(x >> 8, fp) == EOF ||
141       putc(x, fp) == EOF)
142     {
143       perror("write error");
144       exit(1);
145     }
146 }
147
148
149 /*
150  * unsigned 8-bit
151  */
152
153 static void
154 write_u8(FILE *fp, unsigned int x)
155 {
156   if (putc(x, fp) == EOF)
157     {
158       perror("write error");
159       exit(1);
160     }
161 }
162
163
164 /*
165  * Id
166  */
167
168 static void
169 write_id(FILE *fp, Id x)
170 {
171   if (x >= (1 << 14))
172     {
173       if (x >= (1 << 28))
174         putc((x >> 28) | 128, fp);
175       if (x >= (1 << 21))
176         putc((x >> 21) | 128, fp);
177       putc((x >> 14) | 128, fp);
178     }
179   if (x >= (1 << 7))
180     putc((x >> 7) | 128, fp);
181   if (putc(x & 127, fp) == EOF)
182     {
183       perror("write error");
184       exit(1);
185     }
186 }
187
188 static void
189 write_str(FILE *fp, const char *str)
190 {
191   if (fputs (str, fp) == EOF
192       || putc (0, fp) == EOF)
193     {
194       perror("write error");
195       exit(1);
196     }
197 }
198
199 /*
200  * Array of Ids
201  */
202
203 static void
204 write_idarray(FILE *fp, Pool *pool, NeedId *needid, Id *ids)
205 {
206   Id id;
207   if (!ids)
208     return;
209   if (!*ids)
210     {
211       write_u8(fp, 0);
212       return;
213     }
214   for (;;)
215     {
216       id = *ids++;
217       if (needid)
218         id = needid[ISRELDEP(id) ? RELOFF(id) : id].need;
219       if (id >= 64)
220         id = (id & 63) | ((id & ~63) << 1);
221       if (!*ids)
222         {
223           write_id(fp, id);
224           return;
225         }
226       write_id(fp, id | 64);
227     }
228 }
229
230 static int
231 cmp_ids (const void *pa, const void *pb)
232 {
233   Id a = *(Id *)pa;
234   Id b = *(Id *)pb;
235   return a - b;
236 }
237
238 static void
239 write_idarray_sort(FILE *fp, Pool *pool, NeedId *needid, Id *ids)
240 {
241   int len, i;
242   if (!ids)
243     return;
244   if (!*ids)
245     {
246       write_u8 (fp, 0);
247       return;
248     }
249   /* If we ever share idarrays we can't do this in-place.  */
250   for (len = 0; ids[len]; len++)
251     {
252       Id id = ids[len];
253       if (needid)
254         ids[len] = needid[ISRELDEP(id) ? RELOFF(id) : id].need;
255     }
256
257   /* That bloody solvable:prereqmarker needs to stay in position :-(  */
258   Id prereq = SOLVABLE_PREREQMARKER;
259   if (needid)
260     prereq = needid[prereq].need;
261   for (i = 0; i < len; i++)
262     if (ids[i] == prereq)
263       break;
264   if (i > 1)
265     qsort (ids, i, sizeof (Id), cmp_ids);
266   if ((len - i) > 2)
267     qsort (ids + i + 1, len - i - 1, sizeof (Id), cmp_ids);
268
269   Id old = 0;
270   for (i = 0; i < len; i++)
271     /* Ugly PREREQ handling.  A "difference" of 0 is the prereq marker,
272        hence all real differences are offsetted by 1.  Otherwise we would
273        have to handle negative differences, which would cost code space for
274        the encoding of the sign.  We loose the exact mapping of prereq here,
275        but we know the result, so we can recover from that in the reader.  */
276     if (ids[i] == prereq)
277       old = ids[i] = 0;
278     else
279       {
280         ids[i] -= old;
281         old = ids[i] + old;
282         /* XXX If difference is zero we have multiple equal elements,
283            we might want to skip writing them out.  */
284         ids[i]++;
285       }
286
287   /* The differencing above produces many runs of ones and twos.  I tried
288      fairly elaborate schemes to RLE those, but they give only very mediocre
289      improvements in compression, as coding the escapes costs quite some
290      space.  Even if they are coded only as bits in IDs.  The best improvement
291      was about 2.7% for the whole .solv file.  It's probably better to
292      invest some complexity into sharing idarrays, than RLEing.  */
293   for (i = 0; i < len - 1; i++)
294     {
295       Id id = ids[i];
296       if (id >= 64)
297         id = (id & 63) | ((id & ~63) << 1);
298       write_id(fp, id | 64);
299     }
300   old = ids[i];
301   if (old >= 64)
302     old = (old & 63) | ((old & ~63) << 1);
303   write_id(fp, old);
304 }
305
306 static inline void
307 write_id_value(FILE *fp, Id id, Id value, int eof)
308 {
309   if (id >= 64)
310     id = (id & 63) | ((id & ~63) << 1);
311   write_id(fp, id | 64);
312   if (value >= 64)
313     value = (value & 63) | ((value & ~63) << 1);
314   write_id(fp, value | (eof ? 0 : 64));
315 }
316
317 struct schemata {
318   int nschemata;
319   Id *schemadata, *schemadatap;
320   int schemadatafree;
321
322   Id lastschema[256];
323   Id lastschemakey[256];
324 };
325
326 static Id
327 addschema(struct schemata *schemata, Id *schema)
328 {
329   int h, len;
330   Id *sp, schemaid;
331
332   for (sp = schema, len = 0, h = 0; *sp; len++)
333     h = h * 7 + *sp++;
334   h &= 255;
335   len++;
336   if (schemata->lastschema[h] && !memcmp(schemata->schemadata + schemata->lastschema[h], schema, len * sizeof(Id)))
337     return schemata->lastschemakey[h];
338
339   schemaid = 0;
340   for (sp = schemata->schemadata + 1; sp < schemata->schemadatap; )
341     {
342       if (!memcmp(sp, schemata->schemadatap, len * sizeof(Id)))
343         return schemaid;
344       while (*sp++)
345         ;
346       schemaid++;
347     }
348
349   /* a new one */
350   if (len > schemata->schemadatafree)
351     {
352       int l = schemata->schemadatap - schemata->schemadata;
353       schemata->schemadata = xrealloc(schemata->schemadata, (schemata->schemadatap - schemata->schemadata + len + 256) * sizeof(Id));
354       schemata->schemadatafree = len + 256;
355       schemata->schemadatap = schemata->schemadata + l;
356       if (l == 0)
357         {
358           /* leave first one free so that our lastschema test works */
359           *schemata->schemadatap++ = 0;
360           schemata->schemadatafree--;
361         }
362     }
363   if (schemaid != schemata->nschemata)
364     abort();
365   schemata->lastschema[h] = schemata->schemadatap - schemata->schemadata;
366   schemata->lastschemakey[h] = schemaid;
367   memcpy(schemata->schemadatap, schema, len * sizeof(Id));
368   schemata->schemadatafree -= len;
369   schemata->schemadatap += len;
370   schemata->nschemata++;
371   return schemaid;
372 }
373
374 /*
375  * Repo
376  */
377
378 void
379 repo_write(Repo *repo, FILE *fp)
380 {
381   Pool *pool = repo->pool;
382   int i, n;
383   Solvable *s;
384   NeedId *needid;
385   int nstrings, nrels, nkeys;
386   unsigned int sizeid;
387   unsigned int solv_flags;
388   Reldep *ran;
389   Id *idarraydata;
390
391   int idsizes[ID_NUM_INTERNAL];
392   int id2key[ID_NUM_INTERNAL];
393   int nsolvables;
394
395   Id schema[ID_NUM_INTERNAL], *sp;
396   struct schemata schemata;
397   Id *solvschema;       /* schema of our solvables */
398   Id repodataschema, repodataschema_internal;
399
400   nsolvables = 0;
401   idarraydata = repo->idarraydata;
402
403   needid = (NeedId *)xcalloc(pool->ss.nstrings + pool->nrels, sizeof(*needid));
404   memset(idsizes, 0, sizeof(idsizes));
405
406   repodataschema = repodataschema_internal = 0;
407   for (i = 0; i < repo->nrepodata; i++)
408     {
409       int j;
410       idsizes[REPODATA_EXTERNAL] = 1;
411       idsizes[REPODATA_KEYS]++;
412       if (repo->repodata[i].location)
413         {
414           repodataschema = 1;           /* mark that we need it */
415           idsizes[REPODATA_LOCATION] = 1;
416         }
417       else
418         repodataschema_internal = 1;    /* mark that we need it */
419       for (j = 0; j < repo->repodata[i].nkeys; j++)
420         needid[repo->repodata[i].keys[j].name].need++;
421       idsizes[REPODATA_KEYS] += 2 * repo->repodata[i].nkeys;
422     }
423
424   for (i = repo->start, s = pool->solvables + i; i < repo->end; i++, s++)
425     {
426       if (s->repo != repo)
427         continue;
428       nsolvables++;
429       needid[s->name].need++;
430       needid[s->arch].need++;
431       needid[s->evr].need++;
432       if (s->vendor)
433         {
434           needid[s->vendor].need++;
435           idsizes[SOLVABLE_VENDOR] = 1;
436         }
437       if (s->provides)
438         idsizes[SOLVABLE_PROVIDES]    += incneedid(pool, idarraydata + s->provides, needid);
439       if (s->requires)
440         idsizes[SOLVABLE_REQUIRES]    += incneedid(pool, idarraydata + s->requires, needid);
441       if (s->conflicts)
442         idsizes[SOLVABLE_CONFLICTS]   += incneedid(pool, idarraydata + s->conflicts, needid);
443       if (s->obsoletes)
444         idsizes[SOLVABLE_OBSOLETES]   += incneedid(pool, idarraydata + s->obsoletes, needid);
445       if (s->recommends)
446         idsizes[SOLVABLE_RECOMMENDS]  += incneedid(pool, idarraydata + s->recommends, needid);
447       if (s->suggests)
448         idsizes[SOLVABLE_SUGGESTS]    += incneedid(pool, idarraydata + s->suggests, needid);
449       if (s->supplements)
450         idsizes[SOLVABLE_SUPPLEMENTS] += incneedid(pool, idarraydata + s->supplements, needid);
451       if (s->enhances)
452         idsizes[SOLVABLE_ENHANCES]    += incneedid(pool, idarraydata + s->enhances, needid);
453       if (s->freshens)
454         idsizes[SOLVABLE_FRESHENS]    += incneedid(pool, idarraydata + s->freshens, needid);
455     }
456   if (nsolvables != repo->nsolvables)
457     abort();
458
459   idsizes[SOLVABLE_NAME] = 1;
460   idsizes[SOLVABLE_ARCH] = 1;
461   idsizes[SOLVABLE_EVR] = 1;
462   if (repo->rpmdbid)
463     idsizes[RPM_RPMDBID] = 1;
464
465   for (i = SOLVABLE_NAME; i < ID_NUM_INTERNAL; i++)
466     {
467       if (idsizes[i])
468         needid[i].need++;
469     }
470
471   needid[0].need = 0;
472   needid[pool->ss.nstrings].need = 0;
473   for (i = 0; i < pool->ss.nstrings + pool->nrels; i++)
474     needid[i].map = i;
475
476   cmp_pool = pool;
477   qsort(needid + 1, pool->ss.nstrings - 1, sizeof(*needid), needid_cmp_need_s);
478   qsort(needid + pool->ss.nstrings, pool->nrels, sizeof(*needid), needid_cmp_need);
479
480   sizeid = 0;
481   for (i = 1; i < pool->ss.nstrings; i++)
482     {
483       if (!needid[i].need)
484         break;
485       needid[i].need = 0;
486       sizeid += strlen(pool->ss.stringspace + pool->ss.strings[needid[i].map]) + 1;
487     }
488
489   nstrings = i;
490   for (i = 0; i < nstrings; i++)
491     needid[needid[i].map].need = i;
492
493   for (i = 0; i < pool->nrels; i++)
494     {
495       if (!needid[pool->ss.nstrings + i].need)
496         break;
497       else
498         needid[pool->ss.nstrings + i].need = 0;
499     }
500
501   nrels = i;
502   for (i = 0; i < nrels; i++)
503     {
504       needid[needid[pool->ss.nstrings + i].map].need = nstrings + i;
505     }
506
507   /* find the keys we need */
508   nkeys = 1;
509   memset(id2key, 0, sizeof(id2key));
510   for (i = SOLVABLE_NAME; i < ID_NUM_INTERNAL; i++)
511     if (idsizes[i])
512       id2key[i] = nkeys++;
513
514   /* find the schemata we need */
515   memset(&schemata, 0, sizeof(schemata));
516   solvschema = xcalloc(repo->nsolvables, sizeof(Id));
517
518   for (i = repo->start, s = pool->solvables + i, n = 0; i < repo->end; i++, s++)
519     {
520       if (s->repo != repo)
521         continue;
522       sp = schema;
523       *sp++ = SOLVABLE_NAME;
524       *sp++ = SOLVABLE_ARCH;
525       *sp++ = SOLVABLE_EVR;
526       if (s->vendor)
527         *sp++ = SOLVABLE_VENDOR;
528       if (s->provides)
529         *sp++ = SOLVABLE_PROVIDES;
530       if (s->obsoletes)
531         *sp++ = SOLVABLE_OBSOLETES;
532       if (s->conflicts)
533         *sp++ = SOLVABLE_CONFLICTS;
534       if (s->requires)
535         *sp++ = SOLVABLE_REQUIRES;
536       if (s->recommends)
537         *sp++ = SOLVABLE_RECOMMENDS;
538       if (s->suggests)
539         *sp++ = SOLVABLE_SUGGESTS;
540       if (s->supplements)
541         *sp++ = SOLVABLE_SUPPLEMENTS;
542       if (s->enhances)
543         *sp++ = SOLVABLE_ENHANCES;
544       if (s->freshens)
545         *sp++ = SOLVABLE_FRESHENS;
546       if (repo->rpmdbid)
547         *sp++ = RPM_RPMDBID;
548       *sp = 0;
549       solvschema[n++] = addschema(&schemata, schema);
550     }
551
552   if (repodataschema)
553     {
554       /* add us a schema for our repodata */
555       sp = schema;
556       *sp++ = REPODATA_EXTERNAL;
557       *sp++ = REPODATA_KEYS;
558       *sp++ = REPODATA_LOCATION;
559       *sp = 0;
560       repodataschema = addschema(&schemata, schema);
561     }
562   if (repodataschema_internal)
563     {
564       sp = schema;
565       *sp++ = REPODATA_EXTERNAL;
566       *sp++ = REPODATA_KEYS;
567       *sp = 0;
568       repodataschema_internal = addschema(&schemata, schema);
569     }
570
571   /* convert all schemas to local keys */
572   if (schemata.nschemata)
573     for (sp = schemata.schemadata; sp < schemata.schemadatap; sp++)
574       *sp = id2key[*sp];
575
576   /* write file header */
577   write_u32(fp, 'S' << 24 | 'O' << 16 | 'L' << 8 | 'V');
578   write_u32(fp, SOLV_VERSION_3);
579
580   /* write counts */
581   write_u32(fp, nstrings);
582   write_u32(fp, nrels);
583   write_u32(fp, nsolvables);
584   write_u32(fp, nkeys);
585   write_u32(fp, schemata.nschemata);
586   write_u32(fp, repo->nrepodata);  /* info blocks.  */
587   solv_flags = 0;
588   solv_flags |= SOLV_FLAG_PREFIX_POOL;
589 #if 0
590   solv_flags |= SOLV_FLAG_VERTICAL;
591 #endif
592   write_u32(fp, solv_flags);
593
594   /* Build the prefix-encoding of the string pool.  We need to know
595      the size of that before writing it to the file, so we have to
596      build a separate buffer for that.  As it's temporarily possible
597      that this actually is an expansion we can't easily reuse the 
598      stringspace for this.  The max expansion per string is 1 byte,
599      so it will fit into sizeid+nstrings bytes.  */
600   char *prefix = xmalloc (sizeid + nstrings);
601   char *pp = prefix;
602   char *old_str = "";
603   for (i = 1; i < nstrings; i++)
604     {
605       char *str = pool->ss.stringspace + pool->ss.strings[needid[i].map];
606       int same;
607       size_t len;
608       for (same = 0; same < 255; same++)
609         if (old_str[same] != str[same])
610           break;
611       *pp++ = same;
612       len = strlen (str + same) + 1;
613       memcpy (pp, str + same, len);
614       pp += len;
615       old_str = str;
616     }
617
618   /*
619    * write strings
620    */
621   write_u32(fp, sizeid);
622   write_u32(fp, pp - prefix);
623   if (fwrite(prefix, pp - prefix, 1, fp) != 1)
624     {
625       perror("write error");
626       exit(1);
627     }
628   xfree (prefix);
629
630   /*
631    * write RelDeps
632    */
633   for (i = 0; i < nrels; i++)
634     {
635       ran = pool->rels + (needid[pool->ss.nstrings + i].map - pool->ss.nstrings);
636       write_id(fp, needid[ISRELDEP(ran->name) ? RELOFF(ran->name) : ran->name].need);
637       write_id(fp, needid[ISRELDEP(ran->evr) ? RELOFF(ran->evr) : ran->evr].need);
638       write_u8(fp, ran->flags);
639     }
640
641   /*
642    * write keys
643    */
644   for (i = SOLVABLE_NAME; i < ID_NUM_INTERNAL; i++)
645     {
646       if (!idsizes[i])
647         continue;
648       write_id(fp, needid[i].need);
649       if (i >= SOLVABLE_PROVIDES && i <= SOLVABLE_FRESHENS)
650         write_id(fp, TYPE_REL_IDARRAY);
651       else if (i == RPM_RPMDBID)
652         write_id(fp, TYPE_U32);
653       else if (i == REPODATA_EXTERNAL)
654         write_id(fp, TYPE_VOID);
655       else if (i == REPODATA_KEYS)
656         write_id(fp, TYPE_IDVALUEARRAY);
657       else if (i == REPODATA_LOCATION)
658         write_id(fp, TYPE_STR);
659       else
660         write_id(fp, TYPE_ID);
661       write_id(fp, idsizes[i]);
662     }
663
664   /*
665    * write schemata
666    */
667   if (schemata.nschemata)
668     {
669       write_id(fp, schemata.schemadatap - schemata.schemadata - 1);
670       for (sp = schemata.schemadata + 1; sp < schemata.schemadatap; )
671         {
672           write_idarray(fp, pool, 0, sp);
673           while (*sp++)
674             ;
675         }
676     }
677   else
678     write_id(fp, 0);
679
680   /*
681    * write info block
682    */
683   for (i = 0; i < repo->nrepodata; i++)
684     {
685       int j;
686
687       if (repo->repodata[i].location)
688         write_id(fp, repodataschema);
689       else
690         write_id(fp, repodataschema_internal);
691       /* keys + location, write idarray */
692       for (j = 0; j < repo->repodata[i].nkeys; j++)
693         {
694           Id id = needid[repo->repodata[i].keys[j].name].need;
695           write_id_value(fp, id, repo->repodata[i].keys[j].type, j == repo->repodata[i].nkeys - 1);
696         }
697       if (repo->repodata[i].location)
698         write_str(fp, repo->repodata[i].location);
699     }
700
701 #if 0
702   if (1)
703     {
704       Id id, key;
705
706       for (i = 0; i < nsolvables; i++)
707         write_id(fp, solvschema[i]);
708       unsigned char *used = xmalloc(schemata.nschemata);
709       for (id = SOLVABLE_NAME; id <= RPM_RPMDBID; id++)
710         {
711           key = id2key[id];
712           memset(used, 0, nschemata);
713           for (sp = schemata.schemadata + 1, i = 0; sp < schemata.schemadatap; sp++)
714             {
715               if (*sp == 0)
716                 i++;
717               else if (*sp == key)
718                 used[i] = 1;
719             }
720           for (i = repo->start, s = pool->solvables + i, n = 0; i < repo->end; i++, s++)
721             {
722               if (s->repo != repo)
723                 continue;
724               if (!used[solvschema[n++]])
725                 continue;
726               switch(id)
727                 {
728                 case SOLVABLE_NAME:
729                   write_id(fp, needid[s->name].need);
730                   break;
731                 case SOLVABLE_ARCH:
732                   write_id(fp, needid[s->arch].need);
733                   break;
734                 case SOLVABLE_EVR:
735                   write_id(fp, needid[s->evr].need);
736                   break;
737                 case SOLVABLE_VENDOR:
738                   write_id(fp, needid[s->vendor].need);
739                   break;
740                 case RPM_RPMDBID:
741                   write_u32(fp, repo->rpmdbid[i - repo->start]);
742                   break;
743                 case SOLVABLE_PROVIDES:
744                   write_idarray(fp, pool, needid, idarraydata + s->provides);
745                   break;
746                 case SOLVABLE_OBSOLETES:
747                   write_idarray(fp, pool, needid, idarraydata + s->obsoletes);
748                   break;
749                 case SOLVABLE_CONFLICTS:
750                   write_idarray(fp, pool, needid, idarraydata + s->conflicts);
751                   break;
752                 case SOLVABLE_REQUIRES:
753                   write_idarray(fp, pool, needid, idarraydata + s->requires);
754                   break;
755                 case SOLVABLE_RECOMMENDS:
756                   write_idarray(fp, pool, needid, idarraydata + s->recommends);
757                   break;
758                 case SOLVABLE_SUPPLEMENTS:
759                   write_idarray(fp, pool, needid, idarraydata + s->supplements);
760                   break;
761                 case SOLVABLE_SUGGESTS:
762                   write_idarray(fp, pool, needid, idarraydata + s->suggests);
763                   break;
764                 case SOLVABLE_ENHANCES:
765                   write_idarray(fp, pool, needid, idarraydata + s->enhances);
766                   break;
767                 case SOLVABLE_FRESHENS:
768                   write_idarray(fp, pool, needid, idarraydata + s->freshens);
769                   break;
770                 }
771             }
772         }
773       xfree(used);
774       xfree(needid);
775       xfree(solvschema);
776       xfree(schemata.schemadata);
777       return;
778     }
779   
780 #endif
781
782   /*
783    * write Solvables
784    */
785   for (i = repo->start, s = pool->solvables + i, n = 0; i < repo->end; i++, s++)
786     {
787       if (s->repo != repo)
788         continue;
789       /* keep in sync with schema generation! */
790       write_id(fp, solvschema[n++]);
791       write_id(fp, needid[s->name].need);
792       write_id(fp, needid[s->arch].need);
793       write_id(fp, needid[s->evr].need);
794       if (s->vendor)
795         write_id(fp, needid[s->vendor].need);
796       if (s->provides)
797         write_idarray_sort(fp, pool, needid, idarraydata + s->provides);
798       if (s->obsoletes)
799         write_idarray_sort(fp, pool, needid, idarraydata + s->obsoletes);
800       if (s->conflicts)
801         write_idarray_sort(fp, pool, needid, idarraydata + s->conflicts);
802       if (s->requires)
803         write_idarray_sort(fp, pool, needid, idarraydata + s->requires);
804       if (s->recommends)
805         write_idarray_sort(fp, pool, needid, idarraydata + s->recommends);
806       if (s->suggests)
807         write_idarray_sort(fp, pool, needid, idarraydata + s->suggests);
808       if (s->supplements)
809         write_idarray_sort(fp, pool, needid, idarraydata + s->supplements);
810       if (s->enhances)
811         write_idarray_sort(fp, pool, needid, idarraydata + s->enhances);
812       if (s->freshens)
813         write_idarray_sort(fp, pool, needid, idarraydata + s->freshens);
814       if (repo->rpmdbid)
815         write_u32(fp, repo->rpmdbid[i - repo->start]);
816     }
817
818   xfree(needid);
819   xfree(solvschema);
820   xfree(schemata.schemadata);
821 }
822
823 // EOF