- changed repo interface a bit, now alloc/free handle repo pointer and
[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 void
178 repo_add_solv(Repo *repo, FILE *fp)
179 {
180   Pool *pool = repo->pool;
181   int i, j, l;
182   unsigned int numid, numrel, numsolv, numsrcdata, numsolvdata;
183   int numsolvdatabits, type;
184   Offset sizeid;
185   Offset *str;                         /* map Id -> Offset into string space */
186   char *strsp;                         /* repo string space */
187   char *sp;                            /* pointer into string space */
188   Id *idmap;                           /* map of repo Ids to pool Ids */
189   Id id;
190   unsigned int hashmask, h;
191   int hh;
192   Id *hashtbl;
193   Id name, evr, did;
194   int flags;
195   Reldep *ran;
196   SolvData *solvdata;
197   unsigned int size, size_str, size_idarray;
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   pool_freeidhashes(pool);
215
216   numid = read_u32(fp);
217   numrel = read_u32(fp);
218   numsolv= read_u32(fp);
219
220   sizeid = read_u32(fp);               /* size of string+Id space */
221
222   /*
223    * read strings and Ids
224    * 
225    */
226
227   
228   /*
229    * alloc buffers
230    */
231
232   /* alloc string buffer */
233   strsp = (char *)xrealloc(pool->ss.stringspace, pool->ss.sstrings + sizeid + 1);
234   /* alloc string offsets (Id -> Offset into string space) */
235   str = (Offset *)xrealloc(pool->ss.strings, (pool->ss.nstrings + numid) * sizeof(Offset));
236
237   pool->ss.stringspace = strsp;
238   pool->ss.strings = str;                      /* array of offsets into strsp, indexed by Id */
239
240   /* point to _BEHIND_ already allocated string/Id space */
241   strsp += pool->ss.sstrings;
242
243   /* alloc id map for name and rel Ids. this maps ids in the solv files
244    * to the ids in our pool */
245   idmap = (Id *)xcalloc(numid + numrel, sizeof(Id));
246
247   /*
248    * read new repo at end of pool
249    */
250   
251   if (fread(strsp, sizeid, 1, fp) != 1)
252     {
253       fprintf(stderr, "read error while reading strings\n");
254       exit(1);
255     }
256   strsp[sizeid] = 0;                   /* make string space \0 terminated */
257   sp = strsp;
258
259   /*
260    * build hashes for all read strings
261    * 
262    */
263   
264   hashmask = mkmask(pool->ss.nstrings + numid);
265
266 #if 0
267   printf("read %d strings\n", numid);
268   printf("string hash buckets: %d\n", hashmask + 1);
269 #endif
270
271   /*
272    * ensure sufficient hash size
273    */
274   
275   hashtbl = (Id *)xcalloc(hashmask + 1, sizeof(Id));
276
277   /*
278    * fill hashtable with strings already in pool
279    */
280   
281   for (i = 1; i < pool->ss.nstrings; i++)  /* leave out our dummy zero id */
282     {
283       h = strhash(pool->ss.stringspace + pool->ss.strings[i]) & hashmask;
284       hh = HASHCHAIN_START;
285       while (hashtbl[h])
286         h = HASHCHAIN_NEXT(h, hh, hashmask);
287       hashtbl[h] = i;
288     }
289
290   /*
291    * run over string space, calculate offsets
292    * 
293    * build id map (maps solv Id -> pool Id)
294    */
295   
296   for (i = 1; i < numid; i++)
297     {
298       if (sp >= strsp + sizeid)
299         {
300           fprintf(stderr, "not enough strings\n");
301           exit(1);
302         }
303       if (!*sp)                        /* empty string */
304         {
305           idmap[i] = ID_EMPTY;
306           sp++;
307           continue;
308         }
309
310       /* find hash slot */
311       h = strhash(sp) & hashmask;
312       hh = HASHCHAIN_START;
313       for (;;)
314         {
315           id = hashtbl[h];
316           if (id == 0)
317             break;
318           if (!strcmp(pool->ss.stringspace + pool->ss.strings[id], sp))
319             break;                     /* existing string */
320           h = HASHCHAIN_NEXT(h, hh, hashmask);
321         }
322
323       /* length == offset to next string */
324       l = strlen(sp) + 1;
325       if (id == ID_NULL)               /* end of hash chain -> new string */
326         {
327           id = pool->ss.nstrings++;
328           hashtbl[h] = id;
329           str[id] = pool->ss.sstrings;    /* save Offset */
330           if (sp != pool->ss.stringspace + pool->ss.sstrings)   /* not at end-of-buffer */
331             memmove(pool->ss.stringspace + pool->ss.sstrings, sp, l);   /* append to pool buffer */
332           pool->ss.sstrings += l;
333         }
334       idmap[i] = id;                   /* repo relative -> pool relative */
335       sp += l;                         /* next string */
336     }
337   xfree(hashtbl);
338   pool_shrink_strings(pool);           /* vacuum */
339
340   
341   /*
342    * read RelDeps
343    * 
344    */
345   
346   if (numrel)
347     {
348       /* extend rels */
349       ran = (Reldep *)xrealloc(pool->rels, (pool->nrels + numrel) * sizeof(Reldep));
350       if (!ran)
351         {
352           fprintf(stderr, "no mem for rel space\n");
353           exit(1);
354         }
355       pool->rels = ran;        /* extended rel space */
356
357       hashmask = mkmask(pool->nrels + numrel);
358 #if 0
359       printf("read %d rels\n", numrel);
360       printf("rel hash buckets: %d\n", hashmask + 1);
361 #endif
362       /*
363        * prep hash table with already existing RelDeps
364        */
365       
366       hashtbl = (Id *)xcalloc(hashmask + 1, sizeof(Id));
367       for (i = 1; i < pool->nrels; i++)
368         {
369           h = relhash(ran[i].name, ran[i].evr, ran[i].flags) & hashmask;
370           hh = HASHCHAIN_START;
371           while (hashtbl[h])
372             h = HASHCHAIN_NEXT(h, hh, hashmask);
373           hashtbl[h] = i;
374         }
375
376       /*
377        * read RelDeps from repo
378        */
379       
380       for (i = 0; i < numrel; i++)
381         {
382           name = read_id(fp, i + numid);        /* read (repo relative) Ids */
383           evr = read_id(fp, i + numid);
384           flags = read_u8(fp);
385           name = idmap[name];           /* map to (pool relative) Ids */
386           evr = idmap[evr];
387           h = relhash(name, evr, flags) & hashmask;
388           hh = HASHCHAIN_START;
389           for (;;)
390             {
391               id = hashtbl[h];
392               if (id == ID_NULL)        /* end of hash chain */
393                 break;
394               if (ran[id].name == name && ran[id].evr == evr && ran[id].flags == flags)
395                 break;
396               h = HASHCHAIN_NEXT(h, hh, hashmask);
397             }
398           if (id == ID_NULL)            /* new RelDep */
399             {
400               id = pool->nrels++;
401               hashtbl[h] = id;
402               ran[id].name = name;
403               ran[id].evr = evr;
404               ran[id].flags = flags;
405             }
406           idmap[i + numid] = MAKERELDEP(id);   /* fill Id map */
407         }
408       xfree(hashtbl);
409       pool_shrink_rels(pool);           /* vacuum */
410     }
411
412   /*
413    * read (but dont store yet) repo data
414    */
415
416 #if 0
417   printf("read repo data\n");
418 #endif
419   numsrcdata = read_u32(fp);
420   for (i = 0; i < numsrcdata; i++)
421     {
422       type = read_u8(fp);
423       id = idmap[read_id(fp, numid)];
424       switch(type)
425         {
426         case TYPE_ID:
427           read_id(fp, numid + numrel);   /* just check Id */
428           break;
429         case TYPE_U32:
430           read_u32(fp);
431           break;
432         case TYPE_STR:
433           while(read_u8(fp) != 0)
434             ;
435           break;
436         default:
437           fprintf(stderr, "unknown type\n");
438           exit(0);
439         }
440     }
441
442
443   /*
444    * read solvables
445    */
446   
447 #if 0
448   printf("read solvable data info\n");
449 #endif
450   numsolvdata = read_u32(fp);
451   numsolvdatabits = 0;
452   solvdata = (SolvData *)xmalloc(numsolvdata * sizeof(SolvData));
453   size_idarray = 0;
454   size_str = 0;
455
456   for (i = 0; i < numsolvdata; i++)
457     {
458       type = read_u8(fp);
459       solvdata[i].type = type;
460       if ((type & TYPE_BITMAP) != 0)
461         {
462           type ^= TYPE_BITMAP;
463           numsolvdatabits++;
464         }
465       id = idmap[read_id(fp, numid)];
466 #if 0
467       printf("#%d: %s\n", i, id2str(pool, id));
468 #endif
469       solvdata[i].id = id;
470       size = read_u32(fp);
471       solvdata[i].size = size;
472       if (id >= INTERESTED_START && id <= INTERESTED_END)
473         {
474           if (type == TYPE_STR)
475             size_str += size;
476           if (type == TYPE_IDARRAY)
477             size_idarray += size;
478         }
479     }
480
481   if (numsolvdatabits >= 32)
482     {
483       fprintf(stderr, "too many data map bits\n");
484       exit(1);
485     }
486
487   /* make room for our idarrays */
488   if (size_idarray)
489     {
490       repo_reserve_ids(repo, 0, size_idarray);
491       idarraydatap = repo->idarraydata + repo->idarraysize;
492       repo->idarraysize += size_idarray;
493       idarraydataend = repo->idarraydata + repo->idarraysize;
494       repo->lastoff = 0;
495     }
496   else
497     {
498       idarraydatap = 0;
499       idarraydataend = 0;
500     }
501
502   /*
503    * read solvables
504    */
505   
506 #if 0
507   printf("read solvables\n");
508 #endif
509   s = pool_id2solvable(pool, repo_add_solvable_block(repo, numsolv));
510   for (i = 0; i < numsolv; i++, s++)
511     {
512       databits = 0;
513       if (numsolvdatabits)
514         {
515           for (j = 0; j < (numsolvdatabits + 7) >> 3; j++)
516             databits = (databits << 8) | read_u8(fp);
517         }
518       for (j = 0; j < numsolvdata; j++)
519         {
520           type = solvdata[j].type;
521           if ((type & TYPE_BITMAP) != 0)
522             {
523               if (!(databits & 1))
524                 {
525                   databits >>= 1;
526                   continue;
527                 }
528               databits >>= 1;
529               type ^= TYPE_BITMAP;
530             }
531           id = solvdata[j].id;
532           switch (type)
533             {
534             case TYPE_ID:
535               did = idmap[read_id(fp, numid + numrel)];
536               if (id == SOLVABLE_NAME)
537                 s->name = did;
538               else if (id == SOLVABLE_ARCH)
539                 s->arch = did;
540               else if (id == SOLVABLE_EVR)
541                 s->evr = did;
542               else if (id == SOLVABLE_VENDOR)
543                 s->vendor = did;
544 #if 0
545               printf("%s -> %s\n", id2str(pool, id), id2str(pool, did));
546 #endif
547               break;
548             case TYPE_U32:
549               h = read_u32(fp);
550 #if 0
551               printf("%s -> %u\n", id2str(pool, id), h);
552 #endif
553               if (id == RPM_RPMDBID)
554                 {
555                   if (!repo->rpmdbid)
556                     repo->rpmdbid = (Id *)xcalloc(numsolv, sizeof(Id));
557                   repo->rpmdbid[i] = h;
558                 }
559               break;
560             case TYPE_STR:
561               while(read_u8(fp) != 0)
562                 ;
563               break;
564             case TYPE_IDARRAY:
565               if (id < INTERESTED_START || id > INTERESTED_END)
566                 {
567                   /* not interested in array */
568                   while ((read_u8(fp) & 0xc0) != 0)
569                     ;
570                   break;
571                 }
572               ido = idarraydatap - repo->idarraydata;
573               idarraydatap = read_idarray(fp, numid + numrel, idmap, idarraydatap, idarraydataend);
574               if (id == SOLVABLE_PROVIDES)
575                 s->provides = ido;
576               else if (id == SOLVABLE_OBSOLETES)
577                 s->obsoletes = ido;
578               else if (id == SOLVABLE_CONFLICTS)
579                 s->conflicts = ido;
580               else if (id == SOLVABLE_REQUIRES)
581                 s->requires = ido;
582               else if (id == SOLVABLE_RECOMMENDS)
583                 s->recommends= ido;
584               else if (id == SOLVABLE_SUPPLEMENTS)
585                 s->supplements = ido;
586               else if (id == SOLVABLE_SUGGESTS)
587                 s->suggests = ido;
588               else if (id == SOLVABLE_ENHANCES)
589                 s->enhances = ido;
590               else if (id == SOLVABLE_FRESHENS)
591                 s->freshens = ido;
592 #if 0
593               printf("%s ->\n", id2str(pool, id));
594               for (; repo->idarraydata[ido]; ido++)
595                 printf("  %s\n", dep2str(pool, repo->idarraydata[ido]));
596 #endif
597               break;
598             }
599         }
600     }
601   xfree(idmap);
602   xfree(solvdata);
603 }
604
605 // EOF