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 repodata_add_dirnumnum(Repodata *data, Id entry, Id keyname, Id dir, Id num, Id num2)
1050 fprintf(stderr, "repodata_add_dirnumnum %d %d %d %d (%d)\n", entry, dir, num, num2, data->attriddatalen);
1052 if (data->attrs && data->attrs[entry])
1054 for (pp = data->attrs[entry]; *pp; pp += 2)
1055 if (data->keys[*pp].name == keyname && data->keys[*pp].type == REPOKEY_TYPE_DIRNUMNUMARRAY)
1060 for (ida = data->attriddata + pp[1]; *ida; ida += 3)
1062 if (ida + 1 == data->attriddata + data->attriddatalen)
1064 /* this was the last entry, just append it */
1065 data->attriddata = sat_extend(data->attriddata, data->attriddatalen, 3, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1066 data->attriddatalen--; /* overwrite terminating 0 */
1070 /* too bad. move to back. */
1071 data->attriddata = sat_extend(data->attriddata, data->attriddatalen, oldsize + 4, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1072 memcpy(data->attriddata + data->attriddatalen, data->attriddata + pp[1], oldsize * sizeof(Id));
1073 pp[1] = data->attriddatalen;
1074 data->attriddatalen += oldsize;
1076 data->attriddata[data->attriddatalen++] = dir;
1077 data->attriddata[data->attriddatalen++] = num;
1078 data->attriddata[data->attriddatalen++] = num2;
1079 data->attriddata[data->attriddatalen++] = 0;
1084 key.type = REPOKEY_TYPE_DIRNUMNUMARRAY;
1086 key.storage = KEY_STORAGE_INCORE;
1087 data->attriddata = sat_extend(data->attriddata, data->attriddatalen, 4, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1088 repodata_set(data, entry, &key, data->attriddatalen);
1089 data->attriddata[data->attriddatalen++] = dir;
1090 data->attriddata[data->attriddatalen++] = num;
1091 data->attriddata[data->attriddatalen++] = num2;
1092 data->attriddata[data->attriddatalen++] = 0;
1096 repodata_add_dirstr(Repodata *data, Id entry, Id keyname, Id dir, const char *str)
1098 Id *ida, *pp, stroff;
1102 l = strlen(str) + 1;
1103 data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
1104 memcpy(data->attrdata + data->attrdatalen, str, l);
1105 stroff = data->attrdatalen;
1106 data->attrdatalen += l;
1109 fprintf(stderr, "repodata_add_dirstr %d %d %s (%d)\n", entry, dir, str, data->attriddatalen);
1111 if (data->attrs && data->attrs[entry])
1113 for (pp = data->attrs[entry]; *pp; pp += 2)
1114 if (data->keys[*pp].name == keyname && data->keys[*pp].type == REPOKEY_TYPE_DIRSTRARRAY)
1119 for (ida = data->attriddata + pp[1]; *ida; ida += 2)
1121 if (ida + 1 == data->attriddata + data->attriddatalen)
1123 /* this was the last entry, just append it */
1124 data->attriddata = sat_extend(data->attriddata, data->attriddatalen, 2, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1125 data->attriddatalen--; /* overwrite terminating 0 */
1129 /* too bad. move to back. */
1130 data->attriddata = sat_extend(data->attriddata, data->attriddatalen, oldsize + 3, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1131 memcpy(data->attriddata + data->attriddatalen, data->attriddata + pp[1], oldsize * sizeof(Id));
1132 pp[1] = data->attriddatalen;
1133 data->attriddatalen += oldsize;
1135 data->attriddata[data->attriddatalen++] = dir;
1136 data->attriddata[data->attriddatalen++] = stroff;
1137 data->attriddata[data->attriddatalen++] = 0;
1142 key.type = REPOKEY_TYPE_DIRSTRARRAY;
1144 key.storage = KEY_STORAGE_INCORE;
1145 data->attriddata = sat_extend(data->attriddata, data->attriddatalen, 3, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1146 repodata_set(data, entry, &key, data->attriddatalen);
1147 data->attriddata[data->attriddatalen++] = dir;
1148 data->attriddata[data->attriddatalen++] = stroff;
1149 data->attriddata[data->attriddatalen++] = 0;
1153 repodata_add_idarray(Repodata *data, Id entry, Id keyname, Id id)
1159 fprintf(stderr, "repodata_add_idarray %d %d (%d)\n", entry, id, data->attriddatalen);
1161 if (data->attrs && data->attrs[entry])
1163 for (pp = data->attrs[entry]; *pp; pp += 2)
1164 if (data->keys[*pp].name == keyname
1165 && data->keys[*pp].type == REPOKEY_TYPE_IDARRAY)
1170 for (ida = data->attriddata + pp[1]; *ida; ida++)
1172 if (ida + 1 == data->attriddata + data->attriddatalen)
1174 /* this was the last entry, just append it */
1175 data->attriddata = sat_extend(data->attriddata, data->attriddatalen, 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1176 data->attriddatalen--; /* overwrite terminating 0 */
1180 /* too bad. move to back. */
1181 data->attriddata = sat_extend(data->attriddata, data->attriddatalen, oldsize + 2, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1182 memcpy(data->attriddata + data->attriddatalen, data->attriddata + pp[1], oldsize * sizeof(Id));
1183 pp[1] = data->attriddatalen;
1184 data->attriddatalen += oldsize;
1186 data->attriddata[data->attriddatalen++] = id;
1187 data->attriddata[data->attriddatalen++] = 0;
1192 key.type = REPOKEY_TYPE_IDARRAY;
1194 key.storage = KEY_STORAGE_INCORE;
1195 data->attriddata = sat_extend(data->attriddata, data->attriddatalen, 2, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1196 repodata_set(data, entry, &key, data->attriddatalen);
1197 data->attriddata[data->attriddatalen++] = id;
1198 data->attriddata[data->attriddatalen++] = 0;
1202 repodata_add_poolstr_array(Repodata *data, Id entry, Id keyname,
1206 if (data->localpool)
1207 id = stringpool_str2id(&data->spool, str, 1);
1209 id = str2id(data->repo->pool, str, 1);
1210 repodata_add_idarray(data, entry, keyname, id);
1214 repodata_merge_attrs(Repodata *data, Id dest, Id src)
1217 if (dest == src || !(keyp = data->attrs[src]))
1219 for (; *keyp; keyp += 2)
1220 repodata_insert_keyid(data, dest, keyp[0], keyp[1], 0);
1223 /*********************************/
1225 /* unify with repo_write! */
1227 #define EXTDATA_BLOCK 1023
1228 #define SCHEMATA_BLOCK 31
1229 #define SCHEMATADATA_BLOCK 255
1237 data_addid(struct extdata *xd, Id x)
1240 xd->buf = sat_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
1241 dp = xd->buf + xd->len;
1246 *dp++ = (x >> 28) | 128;
1248 *dp++ = (x >> 21) | 128;
1249 *dp++ = (x >> 14) | 128;
1252 *dp++ = (x >> 7) | 128;
1254 xd->len = dp - xd->buf;
1258 data_addideof(struct extdata *xd, Id x, int eof)
1261 x = (x & 63) | ((x & ~63) << 1);
1262 data_addid(xd, (eof ? x: x | 64));
1266 data_addblob(struct extdata *xd, unsigned char *blob, int len)
1268 xd->buf = sat_extend(xd->buf, xd->len, len, 1, EXTDATA_BLOCK);
1269 memcpy(xd->buf + xd->len, blob, len);
1273 /*********************************/
1276 addschema_prepare(Repodata *data, Id *schematacache)
1281 memset(schematacache, 0, 256 * sizeof(Id));
1282 for (i = 0; i < data->nschemata; i++)
1284 for (sp = data->schemadata + data->schemata[i], h = 0; *sp; len++)
1287 schematacache[h] = i + 1;
1289 data->schemadata = sat_extend_resize(data->schemadata, data->schemadatalen, sizeof(Id), SCHEMATADATA_BLOCK);
1290 data->schemata = sat_extend_resize(data->schemata, data->nschemata, sizeof(Id), SCHEMATA_BLOCK);
1294 addschema(Repodata *data, Id *schema, Id *schematacache)
1299 for (sp = schema, len = 0, h = 0; *sp; len++)
1304 cid = schematacache[h];
1308 if (!memcmp(data->schemadata + data->schemata[cid], schema, len * sizeof(Id)))
1310 /* cache conflict */
1311 for (cid = 0; cid < data->nschemata; cid++)
1312 if (!memcmp(data->schemadata + data->schemata[cid], schema, len * sizeof(Id)))
1315 /* a new one. make room. */
1316 data->schemadata = sat_extend(data->schemadata, data->schemadatalen, len, sizeof(Id), SCHEMATADATA_BLOCK);
1317 data->schemata = sat_extend(data->schemata, data->nschemata, 1, sizeof(Id), SCHEMATA_BLOCK);
1319 memcpy(data->schemadata + data->schemadatalen, schema, len * sizeof(Id));
1320 data->schemata[data->nschemata] = data->schemadatalen;
1321 data->schemadatalen += len;
1322 schematacache[h] = data->nschemata + 1;
1324 fprintf(stderr, "addschema: new schema\n");
1326 return data->nschemata++;
1331 repodata_internalize(Repodata *data)
1334 Id id, entry, nentry, *ida;
1335 Id schematacache[256];
1336 Id schemaid, *schema, *sp, oldschema, *keyp, *seen;
1337 unsigned char *dp, *ndp;
1338 int newschema, oldcount;
1339 struct extdata newincore;
1340 struct extdata newvincore;
1345 newvincore.buf = data->vincore;
1346 newvincore.len = data->vincorelen;
1348 schema = sat_malloc2(data->nkeys, sizeof(Id));
1349 seen = sat_malloc2(data->nkeys, sizeof(Id));
1351 /* Merge the data already existing (in data->schemata, ->incoredata and
1352 friends) with the new attributes in data->attrs[]. */
1353 nentry = data->end - data->start;
1354 addschema_prepare(data, schematacache);
1355 memset(&newincore, 0, sizeof(newincore));
1356 data_addid(&newincore, 0);
1357 for (entry = 0; entry < nentry; entry++)
1359 memset(seen, 0, data->nkeys * sizeof(Id));
1361 dp = data->incoredata + data->incoreoffset[entry];
1362 if (data->incoredata)
1363 dp = data_read_id(dp, &oldschema);
1367 fprintf(stderr, "oldschema %d\n", oldschema);
1368 fprintf(stderr, "schemata %d\n", data->schemata[oldschema]);
1369 fprintf(stderr, "schemadata %p\n", data->schemadata);
1371 /* seen: -1: old data 0: skipped >0: id + 1 */
1374 for (keyp = data->schemadata + data->schemata[oldschema]; *keyp; keyp++)
1378 fprintf(stderr, "Inconsistent old data (key occured twice).\n");
1385 if (data->attrs[entry])
1386 for (keyp = data->attrs[entry]; *keyp; keyp += 2)
1393 seen[*keyp] = keyp[1] + 1;
1397 /* Ideally we'd like to sort the new schema here, to ensure
1398 schema equality independend of the ordering. We can't do that
1399 yet. For once see below (old ids need to come before new ids).
1400 An additional difficulty is that we also need to move
1401 the values with the keys. */
1402 schemaid = addschema(data, schema, schematacache);
1404 schemaid = oldschema;
1407 /* Now create data blob. We walk through the (possibly new) schema
1408 and either copy over old data, or insert the new. */
1409 /* XXX Here we rely on the fact that the (new) schema has the form
1410 o1 o2 o3 o4 ... | n1 n2 n3 ...
1411 (oX being the old keyids (possibly overwritten), and nX being
1412 the new keyids). This rules out sorting the keyids in order
1413 to ensure a small schema count. */
1414 data->incoreoffset[entry] = newincore.len;
1415 data_addid(&newincore, schemaid);
1416 for (keyp = data->schemadata + data->schemata[schemaid]; *keyp; keyp++)
1418 key = data->keys + *keyp;
1422 /* Skip the data associated with this old key. */
1423 if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
1425 ndp = data_skip(dp, REPOKEY_TYPE_ID);
1426 ndp = data_skip(ndp, REPOKEY_TYPE_ID);
1428 else if (key->storage == KEY_STORAGE_INCORE)
1429 ndp = data_skip(dp, key->type);
1432 if (seen[*keyp] == -1)
1434 /* If this key was an old one _and_ was not overwritten with
1435 a different value copy over the old value (we skipped it
1438 data_addblob(&newincore, dp, ndp - dp);
1441 else if (seen[*keyp])
1443 /* Otherwise we have a new value. Parse it into the internal
1446 unsigned int oldvincorelen = 0;
1449 if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
1452 oldvincorelen = xd->len;
1454 id = seen[*keyp] - 1;
1457 case REPOKEY_TYPE_VOID:
1458 case REPOKEY_TYPE_CONSTANT:
1459 case REPOKEY_TYPE_CONSTANTID:
1461 case REPOKEY_TYPE_STR:
1462 data_addblob(xd, data->attrdata + id, strlen((char *)(data->attrdata + id)) + 1);
1464 case REPOKEY_TYPE_ID:
1465 case REPOKEY_TYPE_NUM:
1466 case REPOKEY_TYPE_DIR:
1469 case REPOKEY_TYPE_IDARRAY:
1470 for (ida = data->attriddata + id; *ida; ida++)
1471 data_addideof(xd, ida[0], ida[1] ? 0 : 1);
1473 case REPOKEY_TYPE_DIRNUMNUMARRAY:
1474 for (ida = data->attriddata + id; *ida; ida += 3)
1476 data_addid(xd, ida[0]);
1477 data_addid(xd, ida[1]);
1478 data_addideof(xd, ida[2], ida[3] ? 0 : 1);
1481 case REPOKEY_TYPE_DIRSTRARRAY:
1482 for (ida = data->attriddata + id; *ida; ida += 2)
1484 data_addideof(xd, ida[0], ida[2] ? 0 : 1);
1485 data_addblob(xd, data->attrdata + ida[1], strlen((char *)(data->attrdata + ida[1])) + 1);
1489 fprintf(stderr, "don't know how to handle type %d\n", key->type);
1492 if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
1494 /* put offset/len in incore */
1495 data_addid(&newincore, data->lastverticaloffset + oldvincorelen);
1496 oldvincorelen = xd->len - oldvincorelen;
1497 data_addid(&newincore, oldvincorelen);
1502 if (data->attrs[entry])
1503 sat_free(data->attrs[entry]);
1508 sat_free(data->incoredata);
1509 data->incoredata = newincore.buf;
1510 data->incoredatalen = newincore.len;
1511 data->incoredatafree = 0;
1513 sat_free(data->vincore);
1514 data->vincore = newvincore.buf;
1515 data->vincorelen = newvincore.len;
1517 data->attrs = sat_free(data->attrs);
1518 data->attrdata = sat_free(data->attrdata);
1519 data->attriddata = sat_free(data->attriddata);
1520 data->attrdatalen = 0;
1521 data->attriddatalen = 0;
1525 repodata_str2dir(Repodata *data, const char *dir, int create)
1531 while (*dir == '/' && dir[1] == '/')
1533 if (*dir == '/' && !dir[1])
1537 dire = strchrnul(dir, '/');
1538 if (data->localpool)
1539 id = stringpool_strn2id(&data->spool, dir, dire - dir, create);
1541 id = strn2id(data->repo->pool, dir, dire - dir, create);
1544 parent = dirpool_add_dir(&data->dirpool, parent, id, create);
1557 repodata_dir2str(Repodata *data, Id did, const char *suf)
1559 Pool *pool = data->repo->pool;
1566 return suf ? suf : "";
1570 comp = dirpool_compid(&data->dirpool, parent);
1571 comps = stringpool_id2str(data->localpool ? &data->spool : &pool->ss, comp);
1573 parent = dirpool_parent(&data->dirpool, parent);
1578 l += strlen(suf) + 1;
1579 p = pool_alloctmpspace(pool, l + 1) + l;
1590 comp = dirpool_compid(&data->dirpool, parent);
1591 comps = stringpool_id2str(data->localpool ? &data->spool : &pool->ss, comp);
1594 strncpy(p, comps, l);
1595 parent = dirpool_parent(&data->dirpool, parent);
1603 repodata_compress_page(unsigned char *page, unsigned int len, unsigned char *cpage, unsigned int max)
1605 return compress_buf(page, len, cpage, max);
1608 #define SOLV_ERROR_EOF 3
1610 static inline unsigned int
1616 for (i = 0; i < 4; i++)
1626 #define SOLV_ERROR_EOF 3
1627 #define SOLV_ERROR_CORRUPT 6
1629 /* Try to either setup on-demand paging (using FP as backing
1630 file), or in case that doesn't work (FP not seekable) slurps in
1631 all pages and deactivates paging. */
1633 repodata_read_or_setup_pages(Repodata *data, unsigned int pagesz, unsigned int blobsz)
1635 FILE *fp = data->fp;
1636 unsigned int npages;
1638 unsigned int can_seek;
1640 unsigned char buf[BLOB_PAGESIZE];
1642 if (pagesz != BLOB_PAGESIZE)
1644 /* We could handle this by slurping in everything. */
1645 data->error = SOLV_ERROR_CORRUPT;
1649 if ((cur_file_ofs = ftell(fp)) < 0)
1653 data->pagefd = dup(fileno(fp));
1654 if (data->pagefd == -1)
1658 fprintf (stderr, "can %sseek\n", can_seek ? "" : "NOT ");
1660 npages = (blobsz + BLOB_PAGESIZE - 1) / BLOB_PAGESIZE;
1662 data->num_pages = npages;
1663 data->pages = sat_malloc2(npages, sizeof(data->pages[0]));
1665 /* If we can't seek on our input we have to slurp in everything. */
1667 data->blob_store = sat_malloc(npages * BLOB_PAGESIZE);
1668 for (i = 0; i < npages; i++)
1670 unsigned int in_len = read_u32(fp);
1671 unsigned int compressed = in_len & 1;
1672 Attrblobpage *p = data->pages + i;
1675 fprintf (stderr, "page %d: len %d (%scompressed)\n",
1676 i, in_len, compressed ? "" : "not ");
1682 p->file_offset = cur_file_ofs;
1683 p->file_size = in_len * 2 + compressed;
1684 if (fseek(fp, in_len, SEEK_CUR) < 0)
1687 fprintf (stderr, "can't seek after we thought we can\n");
1688 /* We can't fall back to non-seeking behaviour as we already
1689 read over some data pages without storing them away. */
1690 data->error = SOLV_ERROR_EOF;
1691 close(data->pagefd);
1695 cur_file_ofs += in_len;
1699 unsigned int out_len;
1700 void *dest = data->blob_store + i * BLOB_PAGESIZE;
1701 p->mapped_at = i * BLOB_PAGESIZE;
1704 /* We can't seek, so suck everything in. */
1705 if (fread(compressed ? buf : dest, in_len, 1, fp) != 1)
1708 data->error = SOLV_ERROR_EOF;
1713 out_len = unchecked_decompress_buf(buf, in_len, dest, BLOB_PAGESIZE);
1714 if (out_len != BLOB_PAGESIZE && i < npages - 1)
1716 data->error = SOLV_ERROR_CORRUPT;
1725 repodata_disable_paging(Repodata *data)
1727 if (maybe_load_repodata(data, 0)
1729 load_page_range (data, 0, data->num_pages - 1);
1732 vim:cinoptions={.5s,g0,p5,t0,(0,^-0.5s,n-0.5s:tw=78:cindent:sw=4: