- allow repositories that don't consist of a single block of solvables
[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
25 #include "pool.h"
26 #include "repo_write.h"
27
28 /*------------------------------------------------------------------*/
29 /* Id map optimizations */
30
31 typedef struct needid {
32   Id need;
33   Id map;
34 } NeedId;
35
36 /*
37  * increment need Id
38  * idarray: array of Ids, ID_NULL terminated
39  * needid: array of Id->NeedId
40  * 
41  * return count
42  * 
43  */
44
45 static int
46 incneedid(Pool *pool, Id *idarray, NeedId *needid)
47 {
48   if (!idarray)
49     return 0;
50
51   Id id;
52   int n = 0;
53
54   while ((id = *idarray++) != 0)
55     {
56       n++;
57       while (ISRELDEP(id))
58         {
59           Reldep *rd = GETRELDEP(pool, id);
60           needid[GETRELID(pool, id)].need++;
61           if (ISRELDEP(rd->evr))
62             {
63               Id ida[2];
64               ida[0] = rd->evr;
65               ida[1] = 0;
66               incneedid(pool, ida, needid);
67             }
68           else
69             needid[rd->evr].need++;
70           id = rd->name;
71         }
72       needid[id].need++;
73     }
74   return n + 1;
75 }
76
77
78 /*
79  * 
80  */
81
82 static int
83 needid_cmp_need(const void *ap, const void *bp)
84 {
85   const NeedId *a = ap;
86   const NeedId *b = bp;
87   int r;
88   r = b->need - a->need;
89   if (r)
90     return r;
91   return a->map - b->map;
92 }
93
94
95 /*------------------------------------------------------------------*/
96 /* output routines */
97
98 /*
99  * unsigned 32-bit
100  */
101
102 static void
103 write_u32(FILE *fp, unsigned int x)
104 {
105   if (putc(x >> 24, fp) == EOF ||
106       putc(x >> 16, fp) == EOF ||
107       putc(x >> 8, fp) == EOF ||
108       putc(x, fp) == EOF)
109     {
110       perror("write error");
111       exit(1);
112     }
113 }
114
115
116 /*
117  * unsigned 8-bit
118  */
119
120 static void
121 write_u8(FILE *fp, unsigned int x)
122 {
123   if (putc(x, fp) == EOF)
124     {
125       perror("write error");
126       exit(1);
127     }
128 }
129
130
131 /*
132  * Id
133  */
134
135 static void
136 write_id(FILE *fp, Id x)
137 {
138   if (x >= (1 << 14))
139     {
140       if (x >= (1 << 28))
141         putc((x >> 28) | 128, fp);
142       if (x >= (1 << 21))
143         putc((x >> 21) | 128, fp);
144       putc((x >> 14) | 128, fp);
145     }
146   if (x >= (1 << 7))
147     putc((x >> 7) | 128, fp);
148   if (putc(x & 127, fp) == EOF)
149     {
150       perror("write error");
151       exit(1);
152     }
153 }
154
155
156 /*
157  * Array of Ids
158  */
159
160 static void
161 write_idarray(FILE *fp, Pool *pool, NeedId *needid, Id *ids)
162 {
163   Id id;
164   if (!ids)
165     return;
166   if (!*ids)
167     {
168       write_u8(fp, ID_NULL);
169       return;
170     }
171   for (;;)
172     {
173       id = *ids++;
174       id = needid[ISRELDEP(id) ? GETRELID(pool, id) : id].need;
175       if (id >= 64)
176         id = (id & 63) | ((id & ~63) << 1);
177       if (!*ids)
178         {
179           write_id(fp, id);
180           return;
181         }
182       write_id(fp, id | 64);
183     }
184 }
185
186
187 /*
188  * Repo
189  */
190
191 void
192 repo_write(Repo *repo, FILE *fp)
193 {
194   Pool *pool = repo->pool;
195   int i, numsolvdata;
196   Solvable *s;
197   NeedId *needid;
198   int nstrings, nrels;
199   unsigned int sizeid;
200   Reldep *ran;
201   Id *idarraydata;
202
203   int idsizes[RPM_RPMDBID + 1];
204   int bits, bitmaps;
205   int nsolvables;
206
207   nsolvables = 0;
208   idarraydata = repo->idarraydata;
209
210   needid = (NeedId *)calloc(pool->nstrings + pool->nrels, sizeof(*needid));
211
212   memset(idsizes, 0, sizeof(idsizes));
213
214   for (i = repo->start, s = pool->solvables + i; i < repo->end; i++, s++)
215     {
216       if (s->repo != repo)
217         continue;
218       nsolvables++;
219       needid[s->name].need++;
220       needid[s->arch].need++;
221       needid[s->evr].need++;
222       if (s->vendor)
223         {
224           needid[s->vendor].need++;
225           idsizes[SOLVABLE_VENDOR] = 1;
226         }
227       if (s->provides)
228         idsizes[SOLVABLE_PROVIDES]    += incneedid(pool, idarraydata + s->provides, needid);
229       if (s->requires)
230         idsizes[SOLVABLE_REQUIRES]    += incneedid(pool, idarraydata + s->requires, needid);
231       if (s->conflicts)
232         idsizes[SOLVABLE_CONFLICTS]   += incneedid(pool, idarraydata + s->conflicts, needid);
233       if (s->obsoletes)
234         idsizes[SOLVABLE_OBSOLETES]   += incneedid(pool, idarraydata + s->obsoletes, needid);
235       if (s->recommends)
236         idsizes[SOLVABLE_RECOMMENDS]  += incneedid(pool, idarraydata + s->recommends, needid);
237       if (s->suggests)
238         idsizes[SOLVABLE_SUGGESTS]    += incneedid(pool, idarraydata + s->suggests, needid);
239       if (s->supplements)
240         idsizes[SOLVABLE_SUPPLEMENTS] += incneedid(pool, idarraydata + s->supplements, needid);
241       if (s->enhances)
242         idsizes[SOLVABLE_ENHANCES]    += incneedid(pool, idarraydata + s->enhances, needid);
243       if (s->freshens)
244         idsizes[SOLVABLE_FRESHENS]    += incneedid(pool, idarraydata + s->freshens, needid);
245     }
246   if (nsolvables != repo->nsolvables)
247     abort();
248
249   idsizes[SOLVABLE_NAME] = 1;
250   idsizes[SOLVABLE_ARCH] = 1;
251   idsizes[SOLVABLE_EVR] = 1;
252   if (repo->rpmdbid)
253     idsizes[RPM_RPMDBID] = 1;
254
255   for (i = SOLVABLE_NAME; i <= RPM_RPMDBID; i++)
256     {
257       if (idsizes[i])
258         needid[i].need++;
259     }
260
261   needid[0].need = 0;
262   needid[pool->nstrings].need = 0;
263   for (i = 0; i < pool->nstrings + pool->nrels; i++)
264     needid[i].map = i;
265
266   qsort(needid + 1, pool->nstrings - 1, sizeof(*needid), needid_cmp_need);
267   qsort(needid + pool->nstrings, pool->nrels, sizeof(*needid), needid_cmp_need);
268
269   sizeid = 0;
270   for (i = 1; i < pool->nstrings; i++)
271     {
272       if (!needid[i].need)
273         break;
274       needid[i].need = 0;
275       sizeid += strlen(pool->stringspace + pool->strings[needid[i].map]) + 1;
276     }
277
278   nstrings = i;
279   for (i = 0; i < nstrings; i++)
280     needid[needid[i].map].need = i;
281
282   for (i = 0; i < pool->nrels; i++)
283     {
284       if (!needid[pool->nstrings + i].need)
285         break;
286       else
287         needid[pool->nstrings + i].need = 0;
288     }
289
290   nrels = i;
291   for (i = 0; i < nrels; i++)
292     {
293       needid[needid[pool->nstrings + i].map].need = nstrings + i;
294     }
295
296   /* write file header */
297   write_u32(fp, 'S' << 24 | 'O' << 16 | 'L' << 8 | 'V');
298   write_u32(fp, SOLV_VERSION);
299
300   /* write counts */
301   write_u32(fp, nstrings);
302   write_u32(fp, nrels);
303   write_u32(fp, nsolvables);
304   write_u32(fp, sizeid);
305
306   /*
307    * write strings
308    */
309   for (i = 1; i < nstrings; i++)
310     {
311       char *str = pool->stringspace + pool->strings[needid[i].map];
312       if (fwrite(str, strlen(str) + 1, 1, fp) != 1)
313         {
314           perror("write error");
315           exit(1);
316         }
317     }
318
319   /*
320    * write RelDeps
321    */
322   for (i = 0; i < nrels; i++)
323     {
324       ran = pool->rels + (needid[pool->nstrings + i].map - pool->nstrings);
325       write_id(fp, needid[ISRELDEP(ran->name) ? GETRELID(pool, ran->name) : ran->name].need);
326       write_id(fp, needid[ISRELDEP(ran->evr) ? GETRELID(pool, ran->evr) : ran->evr].need);
327       write_u8( fp, ran->flags);
328     }
329
330   write_u32(fp, 0);     /* no repo data */
331
332   /*
333    * write Solvables
334    */
335   
336   numsolvdata = 0;
337   for (i = SOLVABLE_NAME; i <= RPM_RPMDBID; i++)
338     {
339       if (idsizes[i])
340         numsolvdata++;
341     }
342   write_u32(fp, (unsigned int)numsolvdata);
343
344   bitmaps = 0;
345   for (i = SOLVABLE_NAME; i <= SOLVABLE_FRESHENS; i++)
346     {
347       if (!idsizes[i])
348         continue;
349       if (i >= SOLVABLE_PROVIDES && i <= SOLVABLE_FRESHENS)
350         {
351           write_u8(fp, TYPE_IDARRAY|TYPE_BITMAP);
352           bitmaps++;
353         }
354       else
355         write_u8(fp, TYPE_ID);
356       write_id(fp, needid[i].need);
357       if (i >= SOLVABLE_PROVIDES && i <= SOLVABLE_FRESHENS)
358         write_u32(fp, idsizes[i]);
359       else
360         write_u32(fp, 0);
361     }
362
363   if (repo->rpmdbid)
364     {
365       write_u8(fp, TYPE_U32);
366       write_id(fp, needid[RPM_RPMDBID].need);
367       write_u32(fp, 0);
368     }
369
370   for (i = repo->start, s = pool->solvables + i; i < repo->end; i++, s++)
371     {
372       if (s->repo != repo)
373         continue;
374       bits = 0;
375       if (idsizes[SOLVABLE_FRESHENS])
376         bits = (bits << 1) | (s->freshens ? 1 : 0);
377       if (idsizes[SOLVABLE_ENHANCES])
378         bits = (bits << 1) | (s->enhances ? 1 : 0);
379       if (idsizes[SOLVABLE_SUPPLEMENTS])
380         bits = (bits << 1) | (s->supplements ? 1 : 0);
381       if (idsizes[SOLVABLE_SUGGESTS])
382         bits = (bits << 1) | (s->suggests ? 1 : 0);
383       if (idsizes[SOLVABLE_RECOMMENDS])
384         bits = (bits << 1) | (s->recommends ? 1 : 0);
385       if (idsizes[SOLVABLE_REQUIRES])
386         bits = (bits << 1) | (s->requires ? 1 : 0);
387       if (idsizes[SOLVABLE_CONFLICTS])
388         bits = (bits << 1) | (s->conflicts ? 1 : 0);
389       if (idsizes[SOLVABLE_OBSOLETES])
390         bits = (bits << 1) | (s->obsoletes ? 1 : 0);
391       if (idsizes[SOLVABLE_PROVIDES])
392         bits = (bits << 1) | (s->provides ? 1 : 0);
393
394       if (bitmaps > 24)
395         write_u8(fp, bits >> 24);
396       if (bitmaps > 16)
397         write_u8(fp, bits >> 16);
398       if (bitmaps > 8)
399         write_u8(fp, bits >> 8);
400       if (bitmaps)
401         write_u8(fp, bits);
402
403       write_id(fp, needid[s->name].need);
404       write_id(fp, needid[s->arch].need);
405       write_id(fp, needid[s->evr].need);
406       if (idsizes[SOLVABLE_VENDOR])
407         write_id(fp, needid[s->vendor].need);
408
409       if (s->provides)
410         write_idarray(fp, pool, needid, idarraydata + s->provides);
411       if (s->obsoletes)
412         write_idarray(fp, pool, needid, idarraydata + s->obsoletes);
413       if (s->conflicts)
414         write_idarray(fp, pool, needid, idarraydata + s->conflicts);
415       if (s->requires)
416         write_idarray(fp, pool, needid, idarraydata + s->requires);
417       if (s->recommends)
418         write_idarray(fp, pool, needid, idarraydata + s->recommends);
419       if (s->suggests)
420         write_idarray(fp, pool, needid, idarraydata + s->suggests);
421       if (s->supplements)
422         write_idarray(fp, pool, needid, idarraydata + s->supplements);
423       if (s->enhances)
424         write_idarray(fp, pool, needid, idarraydata + s->enhances);
425       if (s->freshens)
426         write_idarray(fp, pool, needid, idarraydata + s->freshens);
427       if (repo->rpmdbid)
428         write_u32(fp, repo->rpmdbid[i]);
429     }
430
431   free(needid);
432 }
433
434 // EOF