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