- add a license
[platform/upstream/libsolv.git] / src / repo_solv.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_solv.c
10  * 
11  * Read the binary dump of a Repo and create a Repo * from it
12  * 
13  *  See
14  *   Repo *pool_addrepo_solv(Pool *pool, FILE *fp)
15  * below
16  * 
17  */
18
19
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <unistd.h>
24 #include <string.h>
25
26 #include "repo_solv.h"
27 #include "util.h"
28
29 #define INTERESTED_START        SOLVABLE_NAME
30 #define INTERESTED_END          SOLVABLE_FRESHENS
31
32 /*-----------------------------------------------------------------*/
33 /* .solv read functions */
34
35 /*
36  * read u32
37  */
38
39 static unsigned int
40 read_u32(FILE *fp)
41 {
42   int c, i;
43   unsigned int x = 0;
44
45   for (i = 0; i < 4; i++)
46     {
47       c = getc(fp);
48       if (c == EOF)
49         {
50           fprintf(stderr, "unexpected EOF\n");
51           exit(1);
52         }
53       x = (x << 8) | c;
54     }
55   return x;
56 }
57
58
59 /*
60  * read u8
61  */
62
63 static unsigned int
64 read_u8(FILE *fp)
65 {
66   int c;
67   c = getc(fp);
68   if (c == EOF)
69     {
70       fprintf(stderr, "unexpected EOF\n");
71       exit(1);
72     }
73   return c;
74 }
75
76
77 /*
78  * read Id
79  */
80
81 static Id
82 read_id(FILE *fp, Id max)
83 {
84   unsigned int x = 0;
85   int c, i;
86
87   for (i = 0; i < 5; i++)
88     {
89       c = getc(fp);
90       if (c == EOF)
91         {
92           fprintf(stderr, "unexpected EOF\n");
93           exit(1);
94         }
95       if (!(c & 128))
96         {
97           x = (x << 7) | c;
98           if (x >= max)
99             {
100               fprintf(stderr, "read_id: id too large (%u/%u)\n", x, max);
101               exit(1);
102             }
103           return x;
104         }
105       x = (x << 7) ^ c ^ 128;
106     }
107   fprintf(stderr, "read_id: id too long\n");
108   exit(1);
109 }
110
111
112 /*
113  * read array of Ids
114  */
115
116 static Id *
117 read_idarray(FILE *fp, Id max, Id *map, Id *store, Id *end)
118 {
119   unsigned int x = 0;
120   int c;
121   for (;;)
122     {
123       c = getc(fp);
124       if (c == EOF)
125         {
126           fprintf(stderr, "unexpected EOF\n");
127           exit(1);
128         }
129       if ((c & 128) == 0)
130         {
131           x = (x << 6) | (c & 63);
132           if (x >= max)
133             {
134               fprintf(stderr, "read_idarray: id too large (%u/%u)\n", x, max);
135               exit(1);
136             }
137           if (store == end)
138             {
139               fprintf(stderr, "read_idarray: array overflow\n");
140               exit(1);
141             }
142           *store++ = map[x];
143           if ((c & 64) == 0)
144             {
145               if (store == end)
146                 {
147                   fprintf(stderr, "read_idarray: array overflow\n");
148                   exit(1);
149                 }
150               *store++ = 0;
151               return store;
152             }
153           x = 0;
154           continue;
155         }
156       x = (x << 7) ^ c ^ 128;
157     }
158 }
159
160
161 /*-----------------------------------------------------------------*/
162
163 typedef struct solvdata {
164   int type;
165   Id id;
166   unsigned int size;
167 } SolvData;
168
169
170 // ----------------------------------------------
171
172 /*
173  * read repo from .solv file
174  *  and add it to pool
175  */
176
177 Repo *
178 pool_addrepo_solv(Pool *pool, FILE *fp, const char *reponame)
179 {
180   int i, j, l;
181   unsigned int numid, numrel, numsolv, numsrcdata, numsolvdata;
182   int numsolvdatabits, type;
183   Offset sizeid;
184   Offset *str;                         /* map Id -> Offset into string space */
185   char *strsp;                         /* repo string space */
186   char *sp;                            /* pointer into string space */
187   Id *idmap;                           /* map of repo Ids to pool Ids */
188   Id id;
189   unsigned int hashmask, h;
190   int hh;
191   Id *hashtbl;
192   Id name, evr, did;
193   int flags;
194   Reldep *ran;
195   SolvData *solvdata;
196   unsigned int size, size_str, size_idarray;
197   Repo *repo;
198   Id *idarraydatap, *idarraydataend;
199   Offset ido;
200   unsigned int databits;
201   Solvable *s;
202
203   if (read_u32(fp) != ('S' << 24 | 'O' << 16 | 'L' << 8 | 'V'))
204     {
205       fprintf(stderr, "not a SOLV file\n");
206       exit(1);
207     }
208   if (read_u32(fp) != SOLV_VERSION)
209     {
210       fprintf(stderr, "unsupported SOLV version\n");
211       exit(1);
212     }
213
214                                        /* create empty Repo */
215   repo = pool_addrepo_empty(pool);
216   pool_freeidhashes(pool);
217
218   repo->name = reponame;
219   
220   numid = read_u32(fp);
221   numrel = read_u32(fp);
222   numsolv= read_u32(fp);
223
224   sizeid = read_u32(fp);               /* size of string+Id space */
225
226   /*
227    * read strings and Ids
228    * 
229    */
230
231   
232   /*
233    * alloc buffers
234    */
235                                        /* alloc string buffer */
236   strsp = (char *)xrealloc(pool->stringspace, pool->sstrings + sizeid + 1);
237                                        /* alloc string offsets (Id -> Offset into string space) */
238   str = (Offset *)xrealloc(pool->strings, (pool->nstrings + numid) * sizeof(Offset));
239
240   pool->stringspace = strsp;
241   pool->strings = str;                 /* array of offsets into strsp, indexed by Id */
242
243   /* point to _BEHIND_ already allocated string/Id space */
244   strsp += pool->sstrings;
245
246                                        /* alloc id map for name and rel Ids */
247   idmap = (Id *)xcalloc(numid + numrel, sizeof(Id));
248
249   /*
250    * read new repo at end of pool
251    */
252   
253   if (fread(strsp, sizeid, 1, fp) != 1)
254     {
255       fprintf(stderr, "read error while reading strings\n");
256       exit(1);
257     }
258   strsp[sizeid] = 0;                   /* make string space \0 terminated */
259   sp = strsp;
260
261   /*
262    * build hashes for all read strings
263    * 
264    */
265   
266   hashmask = mkmask(pool->nstrings + numid);
267
268 #if 0
269   printf("read %d strings\n", numid);
270   printf("string hash buckets: %d\n", hashmask + 1);
271 #endif
272
273   /*
274    * ensure sufficient hash size
275    */
276   
277   hashtbl = (Id *)xcalloc(hashmask + 1, sizeof(Id));
278
279   /*
280    * fill hashtable with strings already in pool
281    */
282   
283   for (i = 1; i < pool->nstrings; i++)  /* leave out our dummy zero id */
284     {
285       h = strhash(pool->stringspace + pool->strings[i]) & hashmask;
286       hh = HASHCHAIN_START;
287       while (hashtbl[h])
288         h = HASHCHAIN_NEXT(h, hh, hashmask);
289       hashtbl[h] = i;
290     }
291
292   /*
293    * run over string space, calculate offsets
294    * 
295    * build id map (maps solv Id -> pool Id)
296    */
297   
298   for (i = 1; i < numid; i++)
299     {
300       if (sp >= strsp + sizeid)
301         {
302           fprintf(stderr, "not enough strings\n");
303           exit(1);
304         }
305       if (!*sp)                        /* empty string */
306         {
307           idmap[i] = ID_EMPTY;
308           sp++;
309           continue;
310         }
311
312       /* find hash slot */
313       h = strhash(sp) & hashmask;
314       hh = HASHCHAIN_START;
315       for (;;)
316         {
317           id = hashtbl[h];
318           if (id == 0)
319             break;
320           if (!strcmp(pool->stringspace + pool->strings[id], sp))
321             break;                     /* existing string */
322           h = HASHCHAIN_NEXT(h, hh, hashmask);
323         }
324
325       /* length == offset to next string */
326       l = strlen(sp) + 1;
327       if (id == ID_NULL)               /* end of hash chain -> new string */
328         {
329           id = pool->nstrings++;
330           hashtbl[h] = id;
331           str[id] = pool->sstrings;    /* save Offset */
332           if (sp != pool->stringspace + pool->sstrings)   /* not at end-of-buffer */
333             memmove(pool->stringspace + pool->sstrings, sp, l);   /* append to pool buffer */
334           pool->sstrings += l;
335         }
336       idmap[i] = id;                   /* repo relative -> pool relative */
337       sp += l;                         /* next string */
338     }
339   xfree(hashtbl);
340   pool_shrink_strings(pool);           /* vacuum */
341
342   
343   /*
344    * read RelDeps
345    * 
346    */
347   
348   if (numrel)
349     {
350       /* extend rels */
351       ran = (Reldep *)xrealloc(pool->rels, (pool->nrels + numrel) * sizeof(Reldep));
352       if (!ran)
353         {
354           fprintf(stderr, "no mem for rel space\n");
355           exit(1);
356         }
357       pool->rels = ran;        /* extended rel space */
358
359       hashmask = mkmask(pool->nrels + numrel);
360 #if 0
361       printf("read %d rels\n", numrel);
362       printf("rel hash buckets: %d\n", hashmask + 1);
363 #endif
364       /*
365        * prep hash table with already existing RelDeps
366        */
367       
368       hashtbl = (Id *)xcalloc(hashmask + 1, sizeof(Id));
369       for (i = 1; i < pool->nrels; i++)
370         {
371           h = relhash(ran[i].name, ran[i].evr, ran[i].flags) & hashmask;
372           hh = HASHCHAIN_START;
373           while (hashtbl[h])
374             h = HASHCHAIN_NEXT(h, hh, hashmask);
375           hashtbl[h] = i;
376         }
377
378       /*
379        * read RelDeps from repo
380        */
381       
382       for (i = 0; i < numrel; i++)
383         {
384           name = read_id(fp, i + numid);        /* read (repo relative) Ids */
385           evr = read_id(fp, i + numid);
386           flags = read_u8(fp);
387           name = idmap[name];           /* map to (pool relative) Ids */
388           evr = idmap[evr];
389           h = relhash(name, evr, flags) & hashmask;
390           hh = HASHCHAIN_START;
391           for (;;)
392             {
393               id = hashtbl[h];
394               if (id == ID_NULL)        /* end of hash chain */
395                 break;
396               if (ran[id].name == name && ran[id].evr == evr && ran[id].flags == flags)
397                 break;
398               h = HASHCHAIN_NEXT(h, hh, hashmask);
399             }
400           if (id == ID_NULL)            /* new RelDep */
401             {
402               id = pool->nrels++;
403               hashtbl[h] = id;
404               ran[id].name = name;
405               ran[id].evr = evr;
406               ran[id].flags = flags;
407             }
408           idmap[i + numid] = MAKERELDEP(id);   /* fill Id map */
409         }
410       xfree(hashtbl);
411       pool_shrink_rels(pool);           /* vacuum */
412     }
413
414   /*
415    * read (but dont store) repo data
416    */
417
418 #if 0
419   printf("read repo data\n");
420 #endif
421   numsrcdata = read_u32(fp);
422   for (i = 0; i < numsrcdata; i++)
423     {
424       type = read_u8(fp);
425       id = idmap[read_id(fp, numid)];
426       switch(type)
427         {
428         case TYPE_ID:
429           read_id(fp, numid + numrel);   /* just check Id */
430           break;
431         case TYPE_U32:
432           read_u32(fp);
433           break;
434         case TYPE_STR:
435           while(read_u8(fp) != 0)
436             ;
437           break;
438         default:
439           fprintf(stderr, "unknown type\n");
440           exit(0);
441         }
442     }
443
444
445   /*
446    * read solvables
447    */
448   
449 #if 0
450   printf("read solvable data info\n");
451 #endif
452   numsolvdata = read_u32(fp);
453   numsolvdatabits = 0;
454   solvdata = (SolvData *)xmalloc(numsolvdata * sizeof(SolvData));
455   size_idarray = 0;
456   size_str = 0;
457
458   for (i = 0; i < numsolvdata; i++)
459     {
460       type = read_u8(fp);
461       solvdata[i].type = type;
462       if ((type & TYPE_BITMAP) != 0)
463         {
464           type ^= TYPE_BITMAP;
465           numsolvdatabits++;
466         }
467       id = idmap[read_id(fp, numid)];
468 #if 0
469       printf("#%d: %s\n", i, id2str(pool, id));
470 #endif
471       solvdata[i].id = id;
472       size = read_u32(fp);
473       solvdata[i].size = size;
474       if (id >= INTERESTED_START && id <= INTERESTED_END)
475         {
476           if (type == TYPE_STR)
477             size_str += size;
478           if (type == TYPE_IDARRAY)
479             size_idarray += size;
480         }
481     }
482
483   if (numsolvdatabits >= 32)
484     {
485       fprintf(stderr, "too many data map bits\n");
486       exit(1);
487     }
488   if (size_idarray)
489     {
490       size_idarray++;   /* first entry is always zero */
491       repo->idarraydata = (Id *)xmalloc(sizeof(Id) * size_idarray);
492       repo->idarraysize = size_idarray;
493       idarraydatap = repo->idarraydata;
494       *idarraydatap++ = 0;
495       idarraydataend = repo->idarraydata + size_idarray;
496     }
497   else
498     {
499       repo->idarraydata = 0;
500       repo->idarraysize = 0;
501       idarraydatap = 0;
502       idarraydataend = 0;
503     }
504
505   /* alloc solvables */
506   pool->solvables = (Solvable *)xrealloc(pool->solvables, (pool->nsolvables + numsolv) * sizeof(Solvable));
507
508   if (numsolv)                         /* clear newly allocated area */
509     memset(pool->solvables + pool->nsolvables, 0, numsolv * sizeof(Solvable));
510   repo->start = pool->nsolvables;
511   repo->nsolvables = numsolv;
512
513   /*
514    * read solvables
515    */
516   
517 #if 0
518   printf("read solvables\n");
519 #endif
520   for (i = 0, s = pool->solvables + repo->start; i < numsolv; i++, s++)
521     {
522       s->repo = repo;
523       databits = 0;
524       if (numsolvdatabits)
525         {
526           for (j = 0; j < (numsolvdatabits + 7) >> 3; j++)
527             databits = (databits << 8) | read_u8(fp);
528         }
529       for (j = 0; j < numsolvdata; j++)
530         {
531           type = solvdata[j].type;
532           if ((type & TYPE_BITMAP) != 0)
533             {
534               if (!(databits & 1))
535                 {
536                   databits >>= 1;
537                   continue;
538                 }
539               databits >>= 1;
540               type ^= TYPE_BITMAP;
541             }
542           id = solvdata[j].id;
543           switch (type)
544             {
545             case TYPE_ID:
546               did = idmap[read_id(fp, numid + numrel)];
547               if (id == SOLVABLE_NAME)
548                 s->name = did;
549               else if (id == SOLVABLE_ARCH)
550                 s->arch = did;
551               else if (id == SOLVABLE_EVR)
552                 s->evr = did;
553               else if (id == SOLVABLE_VENDOR)
554                 s->vendor = did;
555 #if 0
556               printf("%s -> %s\n", id2str(pool, id), id2str(pool, did));
557 #endif
558               break;
559             case TYPE_U32:
560               h = read_u32(fp);
561 #if 0
562               printf("%s -> %u\n", id2str(pool, id), h);
563 #endif
564               if (id == RPM_RPMDBID)
565                 {
566                   if (!repo->rpmdbid)
567                     repo->rpmdbid = (Id *)xcalloc(numsolv, sizeof(Id));
568                   repo->rpmdbid[i] = h;
569                 }
570               break;
571             case TYPE_STR:
572               while(read_u8(fp) != 0)
573                 ;
574               break;
575             case TYPE_IDARRAY:
576               if (id < INTERESTED_START || id > INTERESTED_END)
577                 {
578                   /* not interested in array */
579                   while ((read_u8(fp) & 0xc0) != 0)
580                     ;
581                   break;
582                 }
583               ido = idarraydatap - repo->idarraydata;
584               idarraydatap = read_idarray(fp, numid + numrel, idmap, idarraydatap, idarraydataend);
585               if (id == SOLVABLE_PROVIDES)
586                 s->provides = ido;
587               else if (id == SOLVABLE_OBSOLETES)
588                 s->obsoletes = ido;
589               else if (id == SOLVABLE_CONFLICTS)
590                 s->conflicts = ido;
591               else if (id == SOLVABLE_REQUIRES)
592                 s->requires = ido;
593               else if (id == SOLVABLE_RECOMMENDS)
594                 s->recommends= ido;
595               else if (id == SOLVABLE_SUPPLEMENTS)
596                 s->supplements = ido;
597               else if (id == SOLVABLE_SUGGESTS)
598                 s->suggests = ido;
599               else if (id == SOLVABLE_ENHANCES)
600                 s->enhances = ido;
601               else if (id == SOLVABLE_FRESHENS)
602                 s->freshens = ido;
603 #if 0
604               printf("%s ->\n", id2str(pool, id));
605               for (; repo->idarraydata[ido]; ido++)
606                 printf("  %s\n", dep2str(pool, repo->idarraydata[ido]));
607 #endif
608               break;
609             }
610         }
611     }
612
613   xfree(idmap);
614   xfree(solvdata);
615
616   pool->nsolvables += numsolv;
617
618   return repo;
619 }
620
621 // EOF