2 * Copyright (c) 2007, Novell Inc.
4 * This program is licensed under the BSD license, read LICENSE.BSD
5 * for further information
11 * Manage data coming from one repository
26 #include "poolid_private.h"
31 extern unsigned int compress_buf (const unsigned char *in, unsigned int in_len,
32 unsigned char *out, unsigned int out_len);
33 extern unsigned int unchecked_decompress_buf (const unsigned char *in,
36 unsigned int out_len);
38 #define REPODATA_BLOCK 255
42 repodata_init(Repodata *data, Repo *repo, int localpool)
44 memset(data, 0, sizeof (*data));
46 data->localpool = localpool;
48 stringpool_init_empty(&data->spool);
49 data->keys = sat_calloc(1, sizeof(Repokey));
51 data->schemata = sat_calloc(1, sizeof(Id));
52 data->schemadata = sat_calloc(1, sizeof(Id));
54 data->schemadatalen = 1;
55 data->start = repo->start;
56 data->end = repo->end;
57 data->incoreoffset = sat_extend_resize(0, data->end - data->start, sizeof(Id), REPODATA_BLOCK);
62 repodata_free(Repodata *data)
65 sat_free(data->schemata);
66 sat_free(data->schemadata);
68 sat_free(data->spool.strings);
69 sat_free(data->spool.stringspace);
70 sat_free(data->spool.stringhashtbl);
72 sat_free(data->dirpool.dirs);
73 sat_free(data->dirpool.dirtraverse);
75 sat_free(data->incoredata);
76 sat_free(data->incoreoffset);
77 sat_free(data->verticaloffset);
79 sat_free(data->blob_store);
80 sat_free(data->pages);
81 sat_free(data->mapped);
83 sat_free(data->vincore);
85 sat_free(data->attrs);
86 sat_free(data->attrdata);
87 sat_free(data->attriddata);
89 sat_free(data->location);
90 sat_free(data->addedfileprovides);
92 if (data->pagefd != -1)
96 static unsigned char *
97 forward_to_key(Repodata *data, Id keyid, Id schemaid, unsigned char *dp)
101 keyp = data->schemadata + data->schemata[schemaid];
102 while ((k = *keyp++) != 0)
106 if (data->keys[k].storage == KEY_STORAGE_VERTICAL_OFFSET)
108 dp = data_skip(dp, REPOKEY_TYPE_ID); /* skip that offset */
109 dp = data_skip(dp, REPOKEY_TYPE_ID); /* skip that length */
112 if (data->keys[k].storage != KEY_STORAGE_INCORE)
114 dp = data_skip(dp, data->keys[k].type);
119 #define BLOB_PAGEBITS 15
120 #define BLOB_PAGESIZE (1 << BLOB_PAGEBITS)
122 static unsigned char *
123 load_page_range(Repodata *data, unsigned int pstart, unsigned int pend)
125 /* Make sure all pages from PSTART to PEND (inclusive) are loaded,
126 and are consecutive. Return a pointer to the mapping of PSTART. */
127 unsigned char buf[BLOB_PAGESIZE];
130 /* Quick check in case all pages are there already and consecutive. */
131 for (i = pstart; i <= pend; i++)
132 if (data->pages[i].mapped_at == -1
134 && data->pages[i].mapped_at
135 != data->pages[i-1].mapped_at + BLOB_PAGESIZE))
138 return data->blob_store + data->pages[pstart].mapped_at;
140 if (data->pagefd == -1)
143 /* Ensure that we can map the numbers of pages we need at all. */
144 if (pend - pstart + 1 > data->ncanmap)
146 unsigned int oldcan = data->ncanmap;
147 data->ncanmap = pend - pstart + 1;
148 if (data->ncanmap < 4)
150 data->mapped = sat_realloc2(data->mapped, data->ncanmap, sizeof(data->mapped[0]));
151 memset (data->mapped + oldcan, 0, (data->ncanmap - oldcan) * sizeof (data->mapped[0]));
152 data->blob_store = sat_realloc2(data->blob_store, data->ncanmap, BLOB_PAGESIZE);
154 fprintf (stderr, "PAGE: can map %d pages\n", data->ncanmap);
158 /* Now search for "cheap" space in our store. Space is cheap if it's either
159 free (very cheap) or contains pages we search for anyway. */
161 /* Setup cost array. */
162 unsigned int cost[data->ncanmap];
163 for (i = 0; i < data->ncanmap; i++)
165 unsigned int pnum = data->mapped[i];
171 Attrblobpage *p = data->pages + pnum;
172 assert (p->mapped_at != -1);
173 if (pnum >= pstart && pnum <= pend)
180 /* And search for cheapest space. */
181 unsigned int best_cost = -1;
182 unsigned int best = 0;
183 unsigned int same_cost = 0;
184 for (i = 0; i + pend - pstart < data->ncanmap; i++)
186 unsigned int c = cost[i];
188 for (j = 0; j < pend - pstart + 1; j++)
191 best_cost = c, best = i;
192 else if (c == best_cost)
194 /* A null cost won't become better. */
198 /* If all places have the same cost we would thrash on slot 0. Avoid
199 this by doing a round-robin strategy in this case. */
200 if (same_cost == data->ncanmap - pend + pstart - 1)
201 best = data->rr_counter++ % (data->ncanmap - pend + pstart);
203 /* So we want to map our pages from [best] to [best+pend-pstart].
204 Use a very simple strategy, which doesn't make the best use of
205 our resources, but works. Throw away all pages in that range
206 (even ours) then copy around ours (in case they were outside the
207 range) or read them in. */
208 for (i = best; i < best + pend - pstart + 1; i++)
210 unsigned int pnum = data->mapped[i];
212 /* If this page is exactly at the right place already,
213 no need to evict it. */
214 && pnum != pstart + i - best)
216 /* Evict this page. */
218 fprintf (stderr, "PAGE: evict page %d from %d\n", pnum, i);
222 data->pages[pnum].mapped_at = -1;
226 /* Everything is free now. Read in the pages we want. */
227 for (i = pstart; i <= pend; i++)
229 Attrblobpage *p = data->pages + i;
230 unsigned int pnum = i - pstart + best;
231 void *dest = data->blob_store + pnum * BLOB_PAGESIZE;
232 if (p->mapped_at != -1)
234 if (p->mapped_at != pnum * BLOB_PAGESIZE)
237 fprintf (stderr, "PAGECOPY: %d to %d\n", i, pnum);
239 /* Still mapped somewhere else, so just copy it from there. */
240 memcpy (dest, data->blob_store + p->mapped_at, BLOB_PAGESIZE);
241 data->mapped[p->mapped_at / BLOB_PAGESIZE] = 0;
246 unsigned int in_len = p->file_size;
247 unsigned int compressed = in_len & 1;
250 fprintf (stderr, "PAGEIN: %d to %d", i, pnum);
252 if (pread(data->pagefd, compressed ? buf : dest, in_len, p->file_offset) != in_len)
254 perror ("mapping pread");
259 unsigned int out_len;
260 out_len = unchecked_decompress_buf(buf, in_len,
261 dest, BLOB_PAGESIZE);
262 if (out_len != BLOB_PAGESIZE && i < data->num_pages - 1)
264 fprintf(stderr, "can't decompress\n");
268 fprintf (stderr, " (expand %d to %d)", in_len, out_len);
272 fprintf (stderr, "\n");
275 p->mapped_at = pnum * BLOB_PAGESIZE;
276 data->mapped[pnum] = i + 1;
278 return data->blob_store + best * BLOB_PAGESIZE;
281 static unsigned char *
282 make_vertical_available(Repodata *data, Repokey *key, Id off, Id len)
287 if (off >= data->lastverticaloffset)
289 off -= data->lastverticaloffset;
290 if (off + len > data->vincorelen)
292 return data->vincore + off;
294 if (off + len > key->size)
296 /* we now have the offset, go into vertical */
297 off += data->verticaloffset[key - data->keys];
298 /* fprintf(stderr, "key %d page %d\n", key->name, off / BLOB_PAGESIZE); */
299 dp = load_page_range(data, off / BLOB_PAGESIZE, (off + len - 1) / BLOB_PAGESIZE);
301 dp += off % BLOB_PAGESIZE;
305 static inline unsigned char *
306 get_data(Repodata *data, Repokey *key, unsigned char **dpp)
308 unsigned char *dp = *dpp;
312 if (key->storage == KEY_STORAGE_INCORE)
314 /* hmm, this is a bit expensive */
315 *dpp = data_skip(dp, key->type);
318 else if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
321 dp = data_read_id(dp, &off);
322 dp = data_read_id(dp, &len);
324 return make_vertical_available(data, key, off, len);
330 maybe_load_repodata(Repodata *data, Id *keyid)
332 if (data->state == REPODATA_STUB)
334 if (data->loadcallback)
338 /* key order may change when loading */
340 Id name = data->keys[*keyid].name;
341 Id type = data->keys[*keyid].type;
342 data->loadcallback(data);
343 if (data->state == REPODATA_AVAILABLE)
345 for (i = 1; i < data->nkeys; i++)
346 if (data->keys[i].name == name && data->keys[i].type == type)
355 data->loadcallback(data);
358 data->state = REPODATA_ERROR;
360 if (data->state == REPODATA_AVAILABLE)
362 data->state = REPODATA_ERROR;
367 repodata_lookup_str(Repodata *data, Id entry, Id keyid)
374 if (!maybe_load_repodata(data, &keyid))
377 dp = data->incoredata + data->incoreoffset[entry];
378 dp = data_read_id(dp, &schema);
379 /* make sure the schema of this solvable contains the key */
380 for (keyp = data->schemadata + data->schemata[schema]; *keyp != keyid; keyp++)
383 dp = forward_to_key(data, keyid, schema, dp);
384 key = data->keys + keyid;
385 dp = get_data(data, key, &dp);
388 if (key->type == REPOKEY_TYPE_STR)
389 return (const char *)dp;
390 if (key->type == REPOKEY_TYPE_CONSTANTID)
391 return id2str(data->repo->pool, key->size);
392 if (key->type == REPOKEY_TYPE_ID)
393 dp = data_read_id(dp, &id);
397 return data->spool.stringspace + data->spool.strings[id];
398 return id2str(data->repo->pool, id);
402 repodata_lookup_num(Repodata *data, Id entry, Id keyid, unsigned int *value)
412 if (!maybe_load_repodata(data, &keyid))
415 dp = data->incoredata + data->incoreoffset[entry];
416 dp = data_read_id(dp, &schema);
417 /* make sure the schema of this solvable contains the key */
418 for (keyp = data->schemadata + data->schemata[schema]; *keyp != keyid; keyp++)
421 dp = forward_to_key(data, keyid, schema, dp);
422 key = data->keys + keyid;
423 dp = get_data(data, key, &dp);
426 if (key->type == REPOKEY_TYPE_NUM
427 || key->type == REPOKEY_TYPE_U32
428 || key->type == REPOKEY_TYPE_CONSTANT)
430 dp = data_fetch(dp, &kv, key);
438 repodata_lookup_void(Repodata *data, Id entry, Id keyid)
443 if (!maybe_load_repodata(data, &keyid))
445 dp = data->incoredata + data->incoreoffset[entry];
446 dp = data_read_id(dp, &schema);
447 for (keyp = data->schemadata + data->schemata[schema]; *keyp != keyid; keyp++)
454 repodata_search(Repodata *data, Id entry, Id keyname, int (*callback)(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv), void *cbdata)
458 Id k, keyid, *kp, *keyp;
459 unsigned char *dp, *ddp;
464 if (!maybe_load_repodata(data, 0))
467 dp = data->incoredata + data->incoreoffset[entry];
468 dp = data_read_id(dp, &schema);
469 keyp = data->schemadata + data->schemata[schema];
472 /* search in a specific key */
473 for (kp = keyp; (k = *kp++) != 0; )
474 if (data->keys[k].name == keyname)
478 dp = forward_to_key(data, k, schema, dp);
484 while ((keyid = *keyp++) != 0)
487 key = data->keys + keyid;
488 ddp = get_data(data, key, &dp);
491 ddp = data_fetch(ddp, &kv, key);
494 stop = callback(cbdata, data->repo->pool->solvables + data->start + entry, data, key, &kv);
496 while (!kv.eof && !stop);
497 if (onekey || stop > SEARCH_NEXT_KEY)
503 dataiterator_newdata(Dataiterator *di)
505 Id keyname = di->keyname;
506 Repodata *data = di->data;
509 if (data->state == REPODATA_STUB)
514 for (j = 1; j < data->nkeys; j++)
515 if (keyname == data->keys[j].name)
517 if (j == data->nkeys)
521 if (data->loadcallback)
522 data->loadcallback(data);
524 data->state = REPODATA_ERROR;
526 if (data->state == REPODATA_ERROR)
530 unsigned char *dp = data->incoredata + data->incoreoffset[di->solvid - data->start];
531 dp = data_read_id(dp, &schema);
532 Id *keyp = data->schemadata + data->schemata[schema];
536 /* search in a specific key */
537 for (kp = keyp; (k = *kp++) != 0; )
538 if (data->keys[k].name == keyname)
542 dp = forward_to_key(data, k, schema, dp);
552 di->key = di->data->keys + keyid;
557 di->dp = get_data(di->data, di->key, &di->nextkeydp);
562 dataiterator_init(Dataiterator *di, Repo *repo, Id p, Id keyname,
563 const char *match, int flags)
569 di->flags |= __SEARCH_ONESOLVABLE;
570 di->data = repo->repodata - 1;
571 if (flags & SEARCH_NO_STORAGE_SOLVABLE)
578 di->solvid = repo->start - 1;
579 di->data = repo->repodata + repo->nrepodata - 1;
583 di->keyname = keyname;
584 static Id zeroid = 0;
591 /* FIXME factor and merge with repo_matchvalue */
593 dataiterator_match(Dataiterator *di, KeyValue *kv)
595 int flags = di->flags;
597 if ((flags & SEARCH_STRINGMASK) != 0)
599 switch (di->key->type)
601 case REPOKEY_TYPE_ID:
602 case REPOKEY_TYPE_IDARRAY:
603 if (di->data && di->data->localpool)
604 kv->str = stringpool_id2str(&di->data->spool, kv->id);
606 kv->str = id2str(di->repo->pool, kv->id);
608 case REPOKEY_TYPE_STR:
613 switch ((flags & SEARCH_STRINGMASK))
615 case SEARCH_SUBSTRING:
616 if (flags & SEARCH_NOCASE)
618 if (!strcasestr(kv->str, di->match))
623 if (!strstr(kv->str, di->match))
628 if (flags & SEARCH_NOCASE)
630 if (strcasecmp(di->match, kv->str))
635 if (strcmp(di->match, kv->str))
640 if (fnmatch(di->match, kv->str, (flags & SEARCH_NOCASE) ? FNM_CASEFOLD : 0))
645 if (regexec(&di->regexp, kv->str, 0, NULL, 0))
655 static Repokey solvablekeys[RPM_RPMDBID - SOLVABLE_NAME + 1] = {
656 { SOLVABLE_NAME, REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
657 { SOLVABLE_ARCH, REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
658 { SOLVABLE_EVR, REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
659 { SOLVABLE_VENDOR, REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
660 { SOLVABLE_PROVIDES, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
661 { SOLVABLE_OBSOLETES, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
662 { SOLVABLE_CONFLICTS, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
663 { SOLVABLE_REQUIRES, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
664 { SOLVABLE_RECOMMENDS, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
665 { SOLVABLE_SUGGESTS, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
666 { SOLVABLE_SUPPLEMENTS, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
667 { SOLVABLE_ENHANCES, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
668 { SOLVABLE_FRESHENS, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
669 { RPM_RPMDBID, REPOKEY_TYPE_U32, 0, KEY_STORAGE_SOLVABLE },
673 dataiterator_step(Dataiterator *di)
687 di->kv.eof = idp[1] ? 0 : 1;
693 Solvable *s = di->repo->pool->solvables + di->solvid;
694 int state = di->state;
695 di->key = solvablekeys + state - 1;
697 di->state = RPM_RPMDBID;
704 state = di->keyname - 1;
723 case SOLVABLE_VENDOR:
726 di->kv.id = s->vendor;
728 case SOLVABLE_PROVIDES:
729 di->idp = s->provides
730 ? di->repo->idarraydata + s->provides : 0;
732 case SOLVABLE_OBSOLETES:
733 di->idp = s->obsoletes
734 ? di->repo->idarraydata + s->obsoletes : 0;
736 case SOLVABLE_CONFLICTS:
737 di->idp = s->conflicts
738 ? di->repo->idarraydata + s->conflicts : 0;
740 case SOLVABLE_REQUIRES:
741 di->idp = s->requires
742 ? di->repo->idarraydata + s->requires : 0;
744 case SOLVABLE_RECOMMENDS:
745 di->idp = s->recommends
746 ? di->repo->idarraydata + s->recommends : 0;
748 case SOLVABLE_SUPPLEMENTS:
749 di->idp = s->supplements
750 ? di->repo->idarraydata + s->supplements : 0;
752 case SOLVABLE_SUGGESTS:
753 di->idp = s->suggests
754 ? di->repo->idarraydata + s->suggests : 0;
756 case SOLVABLE_ENHANCES:
757 di->idp = s->enhances
758 ? di->repo->idarraydata + s->enhances : 0;
760 case SOLVABLE_FRESHENS:
761 di->idp = s->freshens
762 ? di->repo->idarraydata + s->freshens : 0;
765 if (!di->repo->rpmdbid)
767 di->kv.num = di->repo->rpmdbid[di->solvid - di->repo->start];
770 di->data = di->repo->repodata - 1;
781 di->dp = data_fetch(di->dp, &di->kv, di->key);
786 if (di->keyname || !(keyid = *di->keyp++))
790 Repo *repo = di->repo;
791 Repodata *data = ++di->data;
792 if (data >= repo->repodata + repo->nrepodata)
794 if (di->flags & __SEARCH_ONESOLVABLE)
796 while (++di->solvid < repo->end)
797 if (repo->pool->solvables[di->solvid].repo == repo)
799 if (di->solvid >= repo->end)
801 di->data = repo->repodata - 1;
802 if (di->flags & SEARCH_NO_STORAGE_SOLVABLE)
804 static Id zeroid = 0;
809 if (di->solvid >= data->start && di->solvid < data->end)
811 dataiterator_newdata(di);
819 di->key = di->data->keys + keyid;
820 di->dp = get_data(di->data, di->key, &di->nextkeydp);
822 di->dp = data_fetch(di->dp, &di->kv, di->key);
827 || dataiterator_match(di, &di->kv))
833 /* extend repodata so that it includes solvables p */
835 repodata_extend(Repodata *data, Id p)
837 if (data->start == data->end)
838 data->start = data->end = p;
841 int old = data->end - data->start;
842 int new = p - data->end + 1;
845 data->attrs = sat_extend(data->attrs, old, new, sizeof(Id *), REPODATA_BLOCK);
846 memset(data->attrs + old, 0, new * sizeof(Id *));
848 data->incoreoffset = sat_extend(data->incoreoffset, old, new, sizeof(Id), REPODATA_BLOCK);
849 memset(data->incoreoffset + old, 0, new * sizeof(Id));
854 int old = data->end - data->start;
855 int new = data->start - p;
858 data->attrs = sat_extend_resize(data->attrs, old + new, sizeof(Id *), REPODATA_BLOCK);
859 memmove(data->attrs + new, data->attrs, old * sizeof(Id *));
860 memset(data->attrs, 0, new * sizeof(Id *));
862 data->incoreoffset = sat_extend_resize(data->incoreoffset, old + new, sizeof(Id), REPODATA_BLOCK);
863 memmove(data->incoreoffset + new, data->incoreoffset, old * sizeof(Id));
864 memset(data->incoreoffset, 0, new * sizeof(Id));
870 repodata_extend_block(Repodata *data, Id start, Id num)
874 if (!data->incoreoffset)
876 data->incoreoffset = sat_calloc_block(num, sizeof(Id), REPODATA_BLOCK);
878 data->end = start + num;
881 repodata_extend(data, start);
883 repodata_extend(data, start + num - 1);
886 /**********************************************************************/
888 #define REPODATA_ATTRS_BLOCK 63
889 #define REPODATA_ATTRDATA_BLOCK 1023
890 #define REPODATA_ATTRIDDATA_BLOCK 63
893 repodata_insert_keyid(Repodata *data, Id entry, Id keyid, Id val, int overwrite)
899 data->attrs = sat_calloc_block(data->end - data->start, sizeof(Id *),
903 if (data->attrs[entry])
905 for (pp = data->attrs[entry]; *pp; pp += 2)
906 /* Determine equality based on the name only, allows us to change
907 type (when overwrite is set), and makes TYPE_CONSTANT work. */
908 if (data->keys[*pp].name == data->keys[keyid].name)
919 i = pp - data->attrs[entry];
921 data->attrs[entry] = sat_extend(data->attrs[entry], i, 3, sizeof(Id), REPODATA_ATTRS_BLOCK);
922 pp = data->attrs[entry] + i;
929 repodata_set(Repodata *data, Id entry, Repokey *key, Id val)
933 /* find key in keys */
934 for (keyid = 1; keyid < data->nkeys; keyid++)
935 if (data->keys[keyid].name == key->name && data->keys[keyid].type == key->type)
937 if ((key->type == REPOKEY_TYPE_CONSTANT || key->type == REPOKEY_TYPE_CONSTANTID) && key->size != data->keys[keyid].size)
941 if (keyid == data->nkeys)
943 /* allocate new key */
944 data->keys = sat_realloc2(data->keys, data->nkeys + 1, sizeof(Repokey));
945 data->keys[data->nkeys++] = *key;
946 if (data->verticaloffset)
948 data->verticaloffset = sat_realloc2(data->verticaloffset, data->nkeys, sizeof(Id));
949 data->verticaloffset[data->nkeys - 1] = 0;
952 repodata_insert_keyid(data, entry, keyid, val, 1);
956 repodata_set_id(Repodata *data, Id entry, Id keyname, Id id)
960 key.type = REPOKEY_TYPE_ID;
962 key.storage = KEY_STORAGE_INCORE;
963 repodata_set(data, entry, &key, id);
967 repodata_set_num(Repodata *data, Id entry, Id keyname, Id num)
971 key.type = REPOKEY_TYPE_NUM;
973 key.storage = KEY_STORAGE_INCORE;
974 repodata_set(data, entry, &key, num);
978 repodata_set_poolstr(Repodata *data, Id entry, Id keyname, const char *str)
983 id = stringpool_str2id(&data->spool, str, 1);
985 id = str2id(data->repo->pool, str, 1);
987 key.type = REPOKEY_TYPE_ID;
989 key.storage = KEY_STORAGE_INCORE;
990 repodata_set(data, entry, &key, id);
994 repodata_set_constant(Repodata *data, Id entry, Id keyname, Id constant)
998 key.type = REPOKEY_TYPE_CONSTANT;
1000 key.storage = KEY_STORAGE_INCORE;
1001 repodata_set(data, entry, &key, 0);
1005 repodata_set_constantid(Repodata *data, Id entry, Id keyname, Id id)
1009 key.type = REPOKEY_TYPE_CONSTANTID;
1011 key.storage = KEY_STORAGE_INCORE;
1012 repodata_set(data, entry, &key, 0);
1016 repodata_set_void(Repodata *data, Id entry, Id keyname)
1020 key.type = REPOKEY_TYPE_VOID;
1022 key.storage = KEY_STORAGE_INCORE;
1023 repodata_set(data, entry, &key, 0);
1027 repodata_set_str(Repodata *data, Id entry, Id keyname, const char *str)
1032 l = strlen(str) + 1;
1034 key.type = REPOKEY_TYPE_STR;
1036 key.storage = KEY_STORAGE_INCORE;
1037 data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
1038 memcpy(data->attrdata + data->attrdatalen, str, l);
1039 repodata_set(data, entry, &key, data->attrdatalen);
1040 data->attrdatalen += l;
1044 repoadata_add_array(Repodata *data, Id entry, Id keyname, Id keytype, int entrysize)
1050 if (data->attrs && data->attrs[entry])
1051 for (pp = data->attrs[entry]; *pp; pp += 2)
1052 if (data->keys[*pp].name == keyname && data->keys[*pp].type == keytype)
1056 /* not found. allocate new key */
1061 key.storage = KEY_STORAGE_INCORE;
1062 data->attriddata = sat_extend(data->attriddata, data->attriddatalen, entrysize + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1063 repodata_set(data, entry, &key, data->attriddatalen);
1067 for (ida = data->attriddata + pp[1]; *ida; ida += entrysize)
1068 oldsize += entrysize;
1069 if (ida + 1 == data->attriddata + data->attriddatalen)
1071 /* this was the last entry, just append it */
1072 data->attriddata = sat_extend(data->attriddata, data->attriddatalen, entrysize, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1073 data->attriddatalen--; /* overwrite terminating 0 */
1077 /* too bad. move to back. */
1078 data->attriddata = sat_extend(data->attriddata, data->attriddatalen, oldsize + entrysize + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1079 memcpy(data->attriddata + data->attriddatalen, data->attriddata + pp[1], oldsize * sizeof(Id));
1080 pp[1] = data->attriddatalen;
1081 data->attriddatalen += oldsize;
1086 repodata_set_bin_checksum(Repodata *data, Id entry, Id keyname, Id type,
1087 const unsigned char *str)
1093 case REPOKEY_TYPE_MD5: l = SIZEOF_MD5; break;
1094 case REPOKEY_TYPE_SHA1: l = SIZEOF_SHA1; break;
1100 key.storage = KEY_STORAGE_INCORE;
1101 data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
1102 memcpy(data->attrdata + data->attrdatalen, str, l);
1103 repodata_set(data, entry, &key, data->attrdatalen);
1104 data->attrdatalen += l;
1108 hexstr2bytes(unsigned char *buf, const char *str, int buflen)
1111 for (i = 0; i < buflen; i++)
1113 #define c2h(c) (((c)>='0' && (c)<='9') ? ((c)-'0') \
1114 : ((c)>='a' && (c)<='f') ? ((c)-'a'+10) \
1115 : ((c)>='A' && (c)<='F') ? ((c)-'A'+10) \
1126 buf[i] = (buf[i] << 4) | v;
1133 repodata_set_checksum(Repodata *data, Id entry, Id keyname, Id type,
1139 case REPOKEY_TYPE_MD5: l = SIZEOF_MD5; break;
1140 case REPOKEY_TYPE_SHA1: l = SIZEOF_SHA1; break;
1143 unsigned char buf[l];
1144 if (hexstr2bytes(buf, str, l) != l)
1146 fprintf(stderr, "Invalid hex character in %s\n", str);
1149 repodata_set_bin_checksum(data, entry, keyname, type, buf);
1153 repodata_chk2str(Repodata *data, Id type, const char *buf)
1159 case REPOKEY_TYPE_MD5: l = SIZEOF_MD5; break;
1160 case REPOKEY_TYPE_SHA1: l = SIZEOF_SHA1; break;
1161 default: return id2str(data->repo->pool, ID_EMPTY);
1163 s = str = pool_alloctmpspace(data->repo->pool, 2*l + 1);
1164 for (i = 0; i < l; i++, s+=2)
1166 unsigned char v = buf[i];
1167 unsigned char w = v >> 4;
1168 s[0] = w >= 10 ? (w-10)+'a' : w + '0';
1170 s[1] = w >= 10 ? (w-10)+'a' : w + '0';
1177 repodata_add_dirnumnum(Repodata *data, Id entry, Id keyname, Id dir, Id num, Id num2)
1181 fprintf(stderr, "repodata_add_dirnumnum %d %d %d %d (%d)\n", entry, dir, num, num2, data->attriddatalen);
1183 repoadata_add_array(data, entry, keyname, REPOKEY_TYPE_DIRNUMNUMARRAY, 3);
1184 data->attriddata[data->attriddatalen++] = dir;
1185 data->attriddata[data->attriddatalen++] = num;
1186 data->attriddata[data->attriddatalen++] = num2;
1187 data->attriddata[data->attriddatalen++] = 0;
1191 repodata_add_dirstr(Repodata *data, Id entry, Id keyname, Id dir, const char *str)
1196 l = strlen(str) + 1;
1197 data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
1198 memcpy(data->attrdata + data->attrdatalen, str, l);
1199 stroff = data->attrdatalen;
1200 data->attrdatalen += l;
1203 fprintf(stderr, "repodata_add_dirstr %d %d %s (%d)\n", entry, dir, str, data->attriddatalen);
1205 repoadata_add_array(data, entry, keyname, REPOKEY_TYPE_DIRSTRARRAY, 2);
1206 data->attriddata[data->attriddatalen++] = dir;
1207 data->attriddata[data->attriddatalen++] = stroff;
1208 data->attriddata[data->attriddatalen++] = 0;
1212 repodata_add_idarray(Repodata *data, Id entry, Id keyname, Id id)
1215 fprintf(stderr, "repodata_add_idarray %d %d (%d)\n", entry, id, data->attriddatalen);
1217 repoadata_add_array(data, entry, keyname, REPOKEY_TYPE_IDARRAY, 1);
1218 data->attriddata[data->attriddatalen++] = id;
1219 data->attriddata[data->attriddatalen++] = 0;
1223 repodata_add_poolstr_array(Repodata *data, Id entry, Id keyname,
1227 if (data->localpool)
1228 id = stringpool_str2id(&data->spool, str, 1);
1230 id = str2id(data->repo->pool, str, 1);
1231 repodata_add_idarray(data, entry, keyname, id);
1235 repodata_merge_attrs(Repodata *data, Id dest, Id src)
1238 if (dest == src || !(keyp = data->attrs[src]))
1240 for (; *keyp; keyp += 2)
1241 repodata_insert_keyid(data, dest, keyp[0], keyp[1], 0);
1244 /*********************************/
1246 /* unify with repo_write! */
1248 #define EXTDATA_BLOCK 1023
1249 #define SCHEMATA_BLOCK 31
1250 #define SCHEMATADATA_BLOCK 255
1258 data_addid(struct extdata *xd, Id x)
1261 xd->buf = sat_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
1262 dp = xd->buf + xd->len;
1267 *dp++ = (x >> 28) | 128;
1269 *dp++ = (x >> 21) | 128;
1270 *dp++ = (x >> 14) | 128;
1273 *dp++ = (x >> 7) | 128;
1275 xd->len = dp - xd->buf;
1279 data_addideof(struct extdata *xd, Id x, int eof)
1282 x = (x & 63) | ((x & ~63) << 1);
1283 data_addid(xd, (eof ? x: x | 64));
1287 data_addblob(struct extdata *xd, unsigned char *blob, int len)
1289 xd->buf = sat_extend(xd->buf, xd->len, len, 1, EXTDATA_BLOCK);
1290 memcpy(xd->buf + xd->len, blob, len);
1294 /*********************************/
1297 addschema_prepare(Repodata *data, Id *schematacache)
1302 memset(schematacache, 0, 256 * sizeof(Id));
1303 for (i = 0; i < data->nschemata; i++)
1305 for (sp = data->schemadata + data->schemata[i], h = 0; *sp; len++)
1308 schematacache[h] = i + 1;
1310 data->schemadata = sat_extend_resize(data->schemadata, data->schemadatalen, sizeof(Id), SCHEMATADATA_BLOCK);
1311 data->schemata = sat_extend_resize(data->schemata, data->nschemata, sizeof(Id), SCHEMATA_BLOCK);
1315 addschema(Repodata *data, Id *schema, Id *schematacache)
1320 for (sp = schema, len = 0, h = 0; *sp; len++)
1325 cid = schematacache[h];
1329 if (!memcmp(data->schemadata + data->schemata[cid], schema, len * sizeof(Id)))
1331 /* cache conflict */
1332 for (cid = 0; cid < data->nschemata; cid++)
1333 if (!memcmp(data->schemadata + data->schemata[cid], schema, len * sizeof(Id)))
1336 /* a new one. make room. */
1337 data->schemadata = sat_extend(data->schemadata, data->schemadatalen, len, sizeof(Id), SCHEMATADATA_BLOCK);
1338 data->schemata = sat_extend(data->schemata, data->nschemata, 1, sizeof(Id), SCHEMATA_BLOCK);
1340 memcpy(data->schemadata + data->schemadatalen, schema, len * sizeof(Id));
1341 data->schemata[data->nschemata] = data->schemadatalen;
1342 data->schemadatalen += len;
1343 schematacache[h] = data->nschemata + 1;
1345 fprintf(stderr, "addschema: new schema\n");
1347 return data->nschemata++;
1352 repodata_internalize(Repodata *data)
1355 Id id, entry, nentry, *ida;
1356 Id schematacache[256];
1357 Id schemaid, *schema, *sp, oldschema, *keyp, *seen;
1358 unsigned char *dp, *ndp;
1359 int newschema, oldcount;
1360 struct extdata newincore;
1361 struct extdata newvincore;
1366 newvincore.buf = data->vincore;
1367 newvincore.len = data->vincorelen;
1369 schema = sat_malloc2(data->nkeys, sizeof(Id));
1370 seen = sat_malloc2(data->nkeys, sizeof(Id));
1372 /* Merge the data already existing (in data->schemata, ->incoredata and
1373 friends) with the new attributes in data->attrs[]. */
1374 nentry = data->end - data->start;
1375 addschema_prepare(data, schematacache);
1376 memset(&newincore, 0, sizeof(newincore));
1377 data_addid(&newincore, 0);
1378 for (entry = 0; entry < nentry; entry++)
1380 memset(seen, 0, data->nkeys * sizeof(Id));
1382 dp = data->incoredata + data->incoreoffset[entry];
1383 if (data->incoredata)
1384 dp = data_read_id(dp, &oldschema);
1388 fprintf(stderr, "oldschema %d\n", oldschema);
1389 fprintf(stderr, "schemata %d\n", data->schemata[oldschema]);
1390 fprintf(stderr, "schemadata %p\n", data->schemadata);
1392 /* seen: -1: old data 0: skipped >0: id + 1 */
1395 for (keyp = data->schemadata + data->schemata[oldschema]; *keyp; keyp++)
1399 fprintf(stderr, "Inconsistent old data (key occured twice).\n");
1406 if (data->attrs[entry])
1407 for (keyp = data->attrs[entry]; *keyp; keyp += 2)
1414 seen[*keyp] = keyp[1] + 1;
1418 /* Ideally we'd like to sort the new schema here, to ensure
1419 schema equality independend of the ordering. We can't do that
1420 yet. For once see below (old ids need to come before new ids).
1421 An additional difficulty is that we also need to move
1422 the values with the keys. */
1423 schemaid = addschema(data, schema, schematacache);
1425 schemaid = oldschema;
1428 /* Now create data blob. We walk through the (possibly new) schema
1429 and either copy over old data, or insert the new. */
1430 /* XXX Here we rely on the fact that the (new) schema has the form
1431 o1 o2 o3 o4 ... | n1 n2 n3 ...
1432 (oX being the old keyids (possibly overwritten), and nX being
1433 the new keyids). This rules out sorting the keyids in order
1434 to ensure a small schema count. */
1435 data->incoreoffset[entry] = newincore.len;
1436 data_addid(&newincore, schemaid);
1437 for (keyp = data->schemadata + data->schemata[schemaid]; *keyp; keyp++)
1439 key = data->keys + *keyp;
1443 /* Skip the data associated with this old key. */
1444 if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
1446 ndp = data_skip(dp, REPOKEY_TYPE_ID);
1447 ndp = data_skip(ndp, REPOKEY_TYPE_ID);
1449 else if (key->storage == KEY_STORAGE_INCORE)
1450 ndp = data_skip(dp, key->type);
1453 if (seen[*keyp] == -1)
1455 /* If this key was an old one _and_ was not overwritten with
1456 a different value copy over the old value (we skipped it
1459 data_addblob(&newincore, dp, ndp - dp);
1462 else if (seen[*keyp])
1464 /* Otherwise we have a new value. Parse it into the internal
1467 unsigned int oldvincorelen = 0;
1470 if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
1473 oldvincorelen = xd->len;
1475 id = seen[*keyp] - 1;
1478 case REPOKEY_TYPE_VOID:
1479 case REPOKEY_TYPE_CONSTANT:
1480 case REPOKEY_TYPE_CONSTANTID:
1482 case REPOKEY_TYPE_STR:
1483 data_addblob(xd, data->attrdata + id, strlen((char *)(data->attrdata + id)) + 1);
1485 case REPOKEY_TYPE_MD5:
1486 data_addblob(xd, data->attrdata + id, SIZEOF_MD5);
1488 case REPOKEY_TYPE_SHA1:
1489 data_addblob(xd, data->attrdata + id, SIZEOF_SHA1);
1491 case REPOKEY_TYPE_ID:
1492 case REPOKEY_TYPE_NUM:
1493 case REPOKEY_TYPE_DIR:
1496 case REPOKEY_TYPE_IDARRAY:
1497 for (ida = data->attriddata + id; *ida; ida++)
1498 data_addideof(xd, ida[0], ida[1] ? 0 : 1);
1500 case REPOKEY_TYPE_DIRNUMNUMARRAY:
1501 for (ida = data->attriddata + id; *ida; ida += 3)
1503 data_addid(xd, ida[0]);
1504 data_addid(xd, ida[1]);
1505 data_addideof(xd, ida[2], ida[3] ? 0 : 1);
1508 case REPOKEY_TYPE_DIRSTRARRAY:
1509 for (ida = data->attriddata + id; *ida; ida += 2)
1511 data_addideof(xd, ida[0], ida[2] ? 0 : 1);
1512 data_addblob(xd, data->attrdata + ida[1], strlen((char *)(data->attrdata + ida[1])) + 1);
1516 fprintf(stderr, "don't know how to handle type %d\n", key->type);
1519 if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
1521 /* put offset/len in incore */
1522 data_addid(&newincore, data->lastverticaloffset + oldvincorelen);
1523 oldvincorelen = xd->len - oldvincorelen;
1524 data_addid(&newincore, oldvincorelen);
1529 if (data->attrs[entry])
1530 sat_free(data->attrs[entry]);
1535 sat_free(data->incoredata);
1536 data->incoredata = newincore.buf;
1537 data->incoredatalen = newincore.len;
1538 data->incoredatafree = 0;
1540 sat_free(data->vincore);
1541 data->vincore = newvincore.buf;
1542 data->vincorelen = newvincore.len;
1544 data->attrs = sat_free(data->attrs);
1545 data->attrdata = sat_free(data->attrdata);
1546 data->attriddata = sat_free(data->attriddata);
1547 data->attrdatalen = 0;
1548 data->attriddatalen = 0;
1552 repodata_str2dir(Repodata *data, const char *dir, int create)
1558 while (*dir == '/' && dir[1] == '/')
1560 if (*dir == '/' && !dir[1])
1564 dire = strchrnul(dir, '/');
1565 if (data->localpool)
1566 id = stringpool_strn2id(&data->spool, dir, dire - dir, create);
1568 id = strn2id(data->repo->pool, dir, dire - dir, create);
1571 parent = dirpool_add_dir(&data->dirpool, parent, id, create);
1584 repodata_dir2str(Repodata *data, Id did, const char *suf)
1586 Pool *pool = data->repo->pool;
1593 return suf ? suf : "";
1597 comp = dirpool_compid(&data->dirpool, parent);
1598 comps = stringpool_id2str(data->localpool ? &data->spool : &pool->ss, comp);
1600 parent = dirpool_parent(&data->dirpool, parent);
1605 l += strlen(suf) + 1;
1606 p = pool_alloctmpspace(pool, l + 1) + l;
1617 comp = dirpool_compid(&data->dirpool, parent);
1618 comps = stringpool_id2str(data->localpool ? &data->spool : &pool->ss, comp);
1621 strncpy(p, comps, l);
1622 parent = dirpool_parent(&data->dirpool, parent);
1630 repodata_compress_page(unsigned char *page, unsigned int len, unsigned char *cpage, unsigned int max)
1632 return compress_buf(page, len, cpage, max);
1635 #define SOLV_ERROR_EOF 3
1637 static inline unsigned int
1643 for (i = 0; i < 4; i++)
1653 #define SOLV_ERROR_EOF 3
1654 #define SOLV_ERROR_CORRUPT 6
1656 /* Try to either setup on-demand paging (using FP as backing
1657 file), or in case that doesn't work (FP not seekable) slurps in
1658 all pages and deactivates paging. */
1660 repodata_read_or_setup_pages(Repodata *data, unsigned int pagesz, unsigned int blobsz)
1662 FILE *fp = data->fp;
1663 unsigned int npages;
1665 unsigned int can_seek;
1667 unsigned char buf[BLOB_PAGESIZE];
1669 if (pagesz != BLOB_PAGESIZE)
1671 /* We could handle this by slurping in everything. */
1672 data->error = SOLV_ERROR_CORRUPT;
1676 if ((cur_file_ofs = ftell(fp)) < 0)
1680 data->pagefd = dup(fileno(fp));
1681 if (data->pagefd == -1)
1685 fprintf (stderr, "can %sseek\n", can_seek ? "" : "NOT ");
1687 npages = (blobsz + BLOB_PAGESIZE - 1) / BLOB_PAGESIZE;
1689 data->num_pages = npages;
1690 data->pages = sat_malloc2(npages, sizeof(data->pages[0]));
1692 /* If we can't seek on our input we have to slurp in everything. */
1694 data->blob_store = sat_malloc(npages * BLOB_PAGESIZE);
1695 for (i = 0; i < npages; i++)
1697 unsigned int in_len = read_u32(fp);
1698 unsigned int compressed = in_len & 1;
1699 Attrblobpage *p = data->pages + i;
1702 fprintf (stderr, "page %d: len %d (%scompressed)\n",
1703 i, in_len, compressed ? "" : "not ");
1709 p->file_offset = cur_file_ofs;
1710 p->file_size = in_len * 2 + compressed;
1711 if (fseek(fp, in_len, SEEK_CUR) < 0)
1714 fprintf (stderr, "can't seek after we thought we can\n");
1715 /* We can't fall back to non-seeking behaviour as we already
1716 read over some data pages without storing them away. */
1717 data->error = SOLV_ERROR_EOF;
1718 close(data->pagefd);
1722 cur_file_ofs += in_len;
1726 unsigned int out_len;
1727 void *dest = data->blob_store + i * BLOB_PAGESIZE;
1728 p->mapped_at = i * BLOB_PAGESIZE;
1731 /* We can't seek, so suck everything in. */
1732 if (fread(compressed ? buf : dest, in_len, 1, fp) != 1)
1735 data->error = SOLV_ERROR_EOF;
1740 out_len = unchecked_decompress_buf(buf, in_len, dest, BLOB_PAGESIZE);
1741 if (out_len != BLOB_PAGESIZE && i < npages - 1)
1743 data->error = SOLV_ERROR_CORRUPT;
1752 repodata_disable_paging(Repodata *data)
1754 if (maybe_load_repodata(data, 0)
1756 load_page_range (data, 0, data->num_pages - 1);
1759 vim:cinoptions={.5s,g0,p5,t0,(0,^-0.5s,n-0.5s:tw=78:cindent:sw=4: