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