- switch to new solv format using schemata
[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 (max && 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           if (map)
145             x = map[x];
146           *store++ = x;
147           if ((c & 64) == 0)
148             {
149               if (store == end)
150                 {
151                   pool_debug(mypool, SAT_FATAL, "read_idarray: array overflow\n");
152                   exit(1);
153                 }
154               *store++ = 0;
155               return store;
156             }
157           x = 0;
158           continue;
159         }
160       x = (x << 7) ^ c ^ 128;
161     }
162 }
163
164
165 /*-----------------------------------------------------------------*/
166
167 struct key {
168   Id name;
169   Id type;
170   Id size;
171 };
172
173
174 // ----------------------------------------------
175
176 /*
177  * read repo from .solv file
178  *  and add it to pool
179  */
180
181 void
182 repo_add_solv(Repo *repo, FILE *fp)
183 {
184   Pool *pool = repo->pool;
185   int i, l;
186   unsigned int numid, numrel, numsolv;
187   unsigned int numkeys, numschemata, numinfo;
188
189   int type;
190   Offset sizeid;
191   Offset *str;                         /* map Id -> Offset into string space */
192   char *strsp;                         /* repo string space */
193   char *sp;                            /* pointer into string space */
194   Id *idmap;                           /* map of repo Ids to pool Ids */
195   Id id;
196   unsigned int hashmask, h;
197   int hh;
198   Id *hashtbl;
199   Id name, evr, did;
200   int flags;
201   Reldep *ran;
202   unsigned int size_idarray;
203   Id *idarraydatap, *idarraydataend;
204   Offset ido;
205   Solvable *s;
206   unsigned int solvflags;
207   struct key *keys;
208   Id *schemadata, *schemadatap, *schemadataend;
209   Id *schemata, key;
210
211   mypool = pool;
212
213   if (read_u32(fp) != ('S' << 24 | 'O' << 16 | 'L' << 8 | 'V'))
214     {
215       pool_debug(pool, SAT_FATAL, "not a SOLV file\n");
216       exit(1);
217     }
218   if (read_u32(fp) != SOLV_VERSION_1)
219     {
220       pool_debug(pool, SAT_FATAL, "unsupported SOLV version\n");
221       exit(1);
222     }
223
224   pool_freeidhashes(pool);
225
226   numid = read_u32(fp);
227   numrel = read_u32(fp);
228   numsolv = read_u32(fp);
229   numkeys = read_u32(fp);
230   numschemata = read_u32(fp);
231   numinfo = read_u32(fp);
232   solvflags = read_u32(fp);
233
234   /*******  Part 1: string IDs  *****************************************/
235
236   sizeid = read_u32(fp);               /* size of string+Id space */
237
238   /*
239    * read strings and Ids
240    * 
241    */
242
243   
244   /*
245    * alloc buffers
246    */
247
248   /* alloc string buffer */
249   strsp = (char *)xrealloc(pool->ss.stringspace, pool->ss.sstrings + sizeid + 1);
250   /* alloc string offsets (Id -> Offset into string space) */
251   str = (Offset *)xrealloc(pool->ss.strings, (pool->ss.nstrings + numid) * sizeof(Offset));
252
253   pool->ss.stringspace = strsp;
254   pool->ss.strings = str;                      /* array of offsets into strsp, indexed by Id */
255
256   /* point to _BEHIND_ already allocated string/Id space */
257   strsp += pool->ss.sstrings;
258
259   /* alloc id map for name and rel Ids. this maps ids in the solv files
260    * to the ids in our pool */
261   idmap = (Id *)xcalloc(numid + numrel, sizeof(Id));
262
263   /*
264    * read new repo at end of pool
265    */
266   
267   if (fread(strsp, sizeid, 1, fp) != 1)
268     {
269       pool_debug(pool, SAT_FATAL, "read error while reading strings\n");
270       exit(1);
271     }
272   strsp[sizeid] = 0;                   /* make string space \0 terminated */
273   sp = strsp;
274
275   /*
276    * build hashes for all read strings
277    * 
278    */
279   
280   hashmask = mkmask(pool->ss.nstrings + numid);
281
282 #if 0
283   POOL_DEBUG(SAT_DEBUG_STATS, "read %d strings\n", numid);
284   POOL_DEBUG(SAT_DEBUG_STATS, "string hash buckets: %d\n", hashmask + 1);
285 #endif
286
287   /*
288    * ensure sufficient hash size
289    */
290   
291   hashtbl = (Id *)xcalloc(hashmask + 1, sizeof(Id));
292
293   /*
294    * fill hashtable with strings already in pool
295    */
296   
297   for (i = 1; i < pool->ss.nstrings; i++)  /* leave out our dummy zero id */
298     {
299       h = strhash(pool->ss.stringspace + pool->ss.strings[i]) & hashmask;
300       hh = HASHCHAIN_START;
301       while (hashtbl[h])
302         h = HASHCHAIN_NEXT(h, hh, hashmask);
303       hashtbl[h] = i;
304     }
305
306   /*
307    * run over string space, calculate offsets
308    * 
309    * build id map (maps solv Id -> pool Id)
310    */
311   
312   for (i = 1; i < numid; i++)
313     {
314       if (sp >= strsp + sizeid)
315         {
316           pool_debug(pool, SAT_FATAL, "not enough strings\n");
317           exit(1);
318         }
319       if (!*sp)                        /* empty string */
320         {
321           idmap[i] = ID_EMPTY;
322           sp++;
323           continue;
324         }
325
326       /* find hash slot */
327       h = strhash(sp) & hashmask;
328       hh = HASHCHAIN_START;
329       for (;;)
330         {
331           id = hashtbl[h];
332           if (id == 0)
333             break;
334           if (!strcmp(pool->ss.stringspace + pool->ss.strings[id], sp))
335             break;                     /* existing string */
336           h = HASHCHAIN_NEXT(h, hh, hashmask);
337         }
338
339       /* length == offset to next string */
340       l = strlen(sp) + 1;
341       if (id == ID_NULL)               /* end of hash chain -> new string */
342         {
343           id = pool->ss.nstrings++;
344           hashtbl[h] = id;
345           str[id] = pool->ss.sstrings;    /* save Offset */
346           if (sp != pool->ss.stringspace + pool->ss.sstrings)   /* not at end-of-buffer */
347             memmove(pool->ss.stringspace + pool->ss.sstrings, sp, l);   /* append to pool buffer */
348           pool->ss.sstrings += l;
349         }
350       idmap[i] = id;                   /* repo relative -> pool relative */
351       sp += l;                         /* next string */
352     }
353   xfree(hashtbl);
354   pool_shrink_strings(pool);           /* vacuum */
355
356   
357   /*******  Part 2: Relation IDs  ***************************************/
358
359   /*
360    * read RelDeps
361    * 
362    */
363   
364   if (numrel)
365     {
366       /* extend rels */
367       ran = (Reldep *)xrealloc(pool->rels, (pool->nrels + numrel) * sizeof(Reldep));
368       if (!ran)
369         {
370           pool_debug(pool, SAT_FATAL, "no mem for rel space\n");
371           exit(1);
372         }
373       pool->rels = ran;        /* extended rel space */
374
375       hashmask = mkmask(pool->nrels + numrel);
376 #if 0
377       POOL_DEBUG(SAT_DEBUG_STATS, "read %d rels\n", numrel);
378       POOL_DEBUG(SAT_DEBUG_STATS, "rel hash buckets: %d\n", hashmask + 1);
379 #endif
380       /*
381        * prep hash table with already existing RelDeps
382        */
383       
384       hashtbl = (Id *)xcalloc(hashmask + 1, sizeof(Id));
385       for (i = 1; i < pool->nrels; i++)
386         {
387           h = relhash(ran[i].name, ran[i].evr, ran[i].flags) & hashmask;
388           hh = HASHCHAIN_START;
389           while (hashtbl[h])
390             h = HASHCHAIN_NEXT(h, hh, hashmask);
391           hashtbl[h] = i;
392         }
393
394       /*
395        * read RelDeps from repo
396        */
397       
398       for (i = 0; i < numrel; i++)
399         {
400           name = read_id(fp, i + numid);        /* read (repo relative) Ids */
401           evr = read_id(fp, i + numid);
402           flags = read_u8(fp);
403           name = idmap[name];           /* map to (pool relative) Ids */
404           evr = idmap[evr];
405           h = relhash(name, evr, flags) & hashmask;
406           hh = HASHCHAIN_START;
407           for (;;)
408             {
409               id = hashtbl[h];
410               if (id == ID_NULL)        /* end of hash chain */
411                 break;
412               if (ran[id].name == name && ran[id].evr == evr && ran[id].flags == flags)
413                 break;
414               h = HASHCHAIN_NEXT(h, hh, hashmask);
415             }
416           if (id == ID_NULL)            /* new RelDep */
417             {
418               id = pool->nrels++;
419               hashtbl[h] = id;
420               ran[id].name = name;
421               ran[id].evr = evr;
422               ran[id].flags = flags;
423             }
424           idmap[i + numid] = MAKERELDEP(id);   /* fill Id map */
425         }
426       xfree(hashtbl);
427       pool_shrink_rels(pool);           /* vacuum */
428     }
429
430
431   /*******  Part 3: Keys  ***********************************************/
432
433   keys = xcalloc(numkeys, sizeof(*keys));
434   /* keys start at 1 */
435   for (i = 1; i < numkeys; i++)
436     {
437       keys[i].name = idmap[read_id(fp, numid)];
438       keys[i].type = read_id(fp, 0);
439       keys[i].size = read_id(fp, 0);
440     }
441
442   /*******  Part 4: Schemata ********************************************/
443   
444   id = read_id(fp, 0);
445   schemadata = xcalloc(id, sizeof(Id));
446   schemadatap = schemadata;
447   schemadataend = schemadata + id;
448   schemata = xcalloc(numschemata, sizeof(Id));
449   for (i = 0; i < numschemata; i++)
450     {
451       schemata[i] = schemadatap - schemadata;
452       schemadatap = read_idarray(fp, numid, 0, schemadatap, schemadataend);
453     }
454
455   /*******  Part 5: Info  ***********************************************/
456   /* we skip the info for now... */
457   for (i = 0; i < numinfo; i++)
458     {
459       Id *schema = schemadata + schemata[read_id(fp, numschemata)];
460       while ((key = *schema++) != 0)
461         {
462           type = keys[key].type;
463           switch (type)
464             {
465               case TYPE_ID:
466                 read_id(fp, numid + numrel);   /* just check Id */
467                 break;
468               case TYPE_U32:
469                 read_u32(fp);
470                 break;
471               case TYPE_STR:
472                 while(read_u8(fp) != 0)
473                   ;
474                 break;
475               case TYPE_IDARRAY:
476                 while ((read_u8(fp) & 0xc0) != 0)
477                   ;
478                 break;
479               default:
480                 pool_debug(pool, SAT_FATAL, "unknown type %d\n", type);
481                 exit(0);
482             }
483         }
484     }
485
486   /*******  Part 6: packed sizes (optional)  ****************************/
487   if ((solvflags & SOLV_FLAG_PACKEDSIZES) != 0)
488     {
489       for (i = 0; i < numsolv; i++)
490         read_id(fp, 0);
491     }
492
493   /*******  Part 7: item data *******************************************/
494
495   /* calculate idarray size */
496   size_idarray = 0;
497   for (i = 1; i < numkeys; i++)
498     {
499       id = keys[i].name;
500       if (keys[i].type == TYPE_IDARRAY && id >= INTERESTED_START && id <= INTERESTED_END)
501         size_idarray += keys[i].size;
502     }
503
504   /* allocate needed space in repo */
505   if (size_idarray)
506     {
507       repo_reserve_ids(repo, 0, size_idarray);
508       idarraydatap = repo->idarraydata + repo->idarraysize;
509       repo->idarraysize += size_idarray;
510       idarraydataend = repo->idarraydata + repo->idarraysize;
511       repo->lastoff = 0;
512     }
513   else
514     {
515       idarraydatap = 0;
516       idarraydataend = 0;
517     }
518
519   /* read solvables */
520   s = pool_id2solvable(pool, repo_add_solvable_block(repo, numsolv));
521
522   if ((solvflags & SOLV_FLAG_VERTICAL) != 0)
523     {
524       Id *solvschema = xcalloc(numsolv, sizeof(Id));
525       unsigned char *used = xmalloc(numschemata);
526       Solvable *sstart = s;
527       Id type;
528
529       for (i = 0; i < numsolv; i++)
530         solvschema[i] = read_id(fp, numschemata);
531       for (key = 1; key < numkeys; key++)
532         {
533           id = keys[key].name;
534           type = keys[key].type;
535           memset(used, 0, numschemata);
536           for (i = 0; i < numschemata; i++)
537             {
538               Id *keyp = schemadata + schemata[i];
539               while (*keyp)
540                 if (*keyp++ == key)
541                   {
542                     used[i] = 1;
543                     break;
544                   }
545             }
546           for (i = 0, s = sstart; i < numsolv; i++, s++)
547              {
548               if (!used[solvschema[i]])
549                 continue;
550               switch (type)
551                 {
552                 case TYPE_ID:
553                   did = idmap[read_id(fp, numid + numrel)];
554                   if (id == SOLVABLE_NAME)
555                     s->name = did;
556                   else if (id == SOLVABLE_ARCH)
557                     s->arch = did;
558                   else if (id == SOLVABLE_EVR)
559                     s->evr = did;
560                   else if (id == SOLVABLE_VENDOR)
561                     s->vendor = did;
562                   break;
563                 case TYPE_U32:
564                   h = read_u32(fp);
565                   if (id == RPM_RPMDBID)
566                     {
567                       if (!repo->rpmdbid)
568                         repo->rpmdbid = (Id *)xcalloc(numsolv, sizeof(Id));
569                       repo->rpmdbid[i] = h;
570                     }
571                   break;
572                 case TYPE_STR:
573                   while(read_u8(fp) != 0)
574                     ;
575                   break;
576                 case TYPE_IDARRAY:
577                   if (id < INTERESTED_START || id > INTERESTED_END)
578                     {
579                       /* not interested in array */
580                       while ((read_u8(fp) & 0xc0) != 0)
581                         ;
582                       break;
583                     }
584                   ido = idarraydatap - repo->idarraydata;
585                   idarraydatap = read_idarray(fp, numid + numrel, idmap, idarraydatap, idarraydataend);
586                   if (id == SOLVABLE_PROVIDES)
587                     s->provides = ido;
588                   else if (id == SOLVABLE_OBSOLETES)
589                     s->obsoletes = ido;
590                   else if (id == SOLVABLE_CONFLICTS)
591                     s->conflicts = ido;
592                   else if (id == SOLVABLE_REQUIRES)
593                     s->requires = ido;
594                   else if (id == SOLVABLE_RECOMMENDS)
595                     s->recommends= ido;
596                   else if (id == SOLVABLE_SUPPLEMENTS)
597                     s->supplements = ido;
598                   else if (id == SOLVABLE_SUGGESTS)
599                     s->suggests = ido;
600                   else if (id == SOLVABLE_ENHANCES)
601                     s->enhances = ido;
602                   else if (id == SOLVABLE_FRESHENS)
603                     s->freshens = ido;
604                   break;
605                 }
606             }
607         }
608       xfree(used);
609       xfree(solvschema);
610       xfree(idmap);
611       xfree(schemata);
612       xfree(schemadata);
613       xfree(keys);
614       mypool = 0;
615       return;
616     }
617   for (i = 0; i < numsolv; i++, s++)
618     {
619       Id *keyp = schemadata + schemata[read_id(fp, numschemata)];
620       while ((key = *keyp++) != 0)
621         {
622           id = keys[key].name;
623           switch (keys[key].type)
624             {
625             case TYPE_ID:
626               did = idmap[read_id(fp, numid + numrel)];
627               if (id == SOLVABLE_NAME)
628                 s->name = did;
629               else if (id == SOLVABLE_ARCH)
630                 s->arch = did;
631               else if (id == SOLVABLE_EVR)
632                 s->evr = did;
633               else if (id == SOLVABLE_VENDOR)
634                 s->vendor = did;
635 #if 0
636               POOL_DEBUG(SAT_DEBUG_STATS, "%s -> %s\n", id2str(pool, id), id2str(pool, did));
637 #endif
638               break;
639             case TYPE_U32:
640               h = read_u32(fp);
641 #if 0
642               POOL_DEBUG(SAT_DEBUG_STATS, "%s -> %u\n", id2str(pool, id), h);
643 #endif
644               if (id == RPM_RPMDBID)
645                 {
646                   if (!repo->rpmdbid)
647                     repo->rpmdbid = (Id *)xcalloc(numsolv, sizeof(Id));
648                   repo->rpmdbid[i] = h;
649                 }
650               break;
651             case TYPE_STR:
652               while(read_u8(fp) != 0)
653                 ;
654               break;
655             case TYPE_IDARRAY:
656               if (id < INTERESTED_START || id > INTERESTED_END)
657                 {
658                   /* not interested in array */
659                   while ((read_u8(fp) & 0xc0) != 0)
660                     ;
661                   break;
662                 }
663               ido = idarraydatap - repo->idarraydata;
664               idarraydatap = read_idarray(fp, numid + numrel, idmap, idarraydatap, idarraydataend);
665               if (id == SOLVABLE_PROVIDES)
666                 s->provides = ido;
667               else if (id == SOLVABLE_OBSOLETES)
668                 s->obsoletes = ido;
669               else if (id == SOLVABLE_CONFLICTS)
670                 s->conflicts = ido;
671               else if (id == SOLVABLE_REQUIRES)
672                 s->requires = ido;
673               else if (id == SOLVABLE_RECOMMENDS)
674                 s->recommends= ido;
675               else if (id == SOLVABLE_SUPPLEMENTS)
676                 s->supplements = ido;
677               else if (id == SOLVABLE_SUGGESTS)
678                 s->suggests = ido;
679               else if (id == SOLVABLE_ENHANCES)
680                 s->enhances = ido;
681               else if (id == SOLVABLE_FRESHENS)
682                 s->freshens = ido;
683 #if 0
684               POOL_DEBUG(SAT_DEBUG_STATS, "%s ->\n", id2str(pool, id));
685               for (; repo->idarraydata[ido]; ido++)
686                 POOL_DEBUG(SAT_DEBUG_STATS,"  %s\n", dep2str(pool, repo->idarraydata[ido]));
687 #endif
688               break;
689             }
690         }
691     }
692   xfree(idmap);
693   xfree(schemata);
694   xfree(schemadata);
695   xfree(keys);
696   mypool = 0;
697 }
698
699 // EOF