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_add_dirnumnum(Repodata *data, Id entry, Id keyname, Id dir, Id num, Id num2)
1090 fprintf(stderr, "repodata_add_dirnumnum %d %d %d %d (%d)\n", entry, dir, num, num2, data->attriddatalen);
1092 repoadata_add_array(data, entry, keyname, REPOKEY_TYPE_DIRNUMNUMARRAY, 3);
1093 data->attriddata[data->attriddatalen++] = dir;
1094 data->attriddata[data->attriddatalen++] = num;
1095 data->attriddata[data->attriddatalen++] = num2;
1096 data->attriddata[data->attriddatalen++] = 0;
1100 repodata_add_dirstr(Repodata *data, Id entry, Id keyname, Id dir, const char *str)
1105 l = strlen(str) + 1;
1106 data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
1107 memcpy(data->attrdata + data->attrdatalen, str, l);
1108 stroff = data->attrdatalen;
1109 data->attrdatalen += l;
1112 fprintf(stderr, "repodata_add_dirstr %d %d %s (%d)\n", entry, dir, str, data->attriddatalen);
1114 repoadata_add_array(data, entry, keyname, REPOKEY_TYPE_DIRSTRARRAY, 2);
1115 data->attriddata[data->attriddatalen++] = dir;
1116 data->attriddata[data->attriddatalen++] = stroff;
1117 data->attriddata[data->attriddatalen++] = 0;
1121 repodata_add_idarray(Repodata *data, Id entry, Id keyname, Id id)
1124 fprintf(stderr, "repodata_add_idarray %d %d (%d)\n", entry, id, data->attriddatalen);
1126 repoadata_add_array(data, entry, keyname, REPOKEY_TYPE_IDARRAY, 1);
1127 data->attriddata[data->attriddatalen++] = id;
1128 data->attriddata[data->attriddatalen++] = 0;
1132 repodata_add_poolstr_array(Repodata *data, Id entry, Id keyname,
1136 if (data->localpool)
1137 id = stringpool_str2id(&data->spool, str, 1);
1139 id = str2id(data->repo->pool, str, 1);
1140 repodata_add_idarray(data, entry, keyname, id);
1144 repodata_merge_attrs(Repodata *data, Id dest, Id src)
1147 if (dest == src || !(keyp = data->attrs[src]))
1149 for (; *keyp; keyp += 2)
1150 repodata_insert_keyid(data, dest, keyp[0], keyp[1], 0);
1153 /*********************************/
1155 /* unify with repo_write! */
1157 #define EXTDATA_BLOCK 1023
1158 #define SCHEMATA_BLOCK 31
1159 #define SCHEMATADATA_BLOCK 255
1167 data_addid(struct extdata *xd, Id x)
1170 xd->buf = sat_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
1171 dp = xd->buf + xd->len;
1176 *dp++ = (x >> 28) | 128;
1178 *dp++ = (x >> 21) | 128;
1179 *dp++ = (x >> 14) | 128;
1182 *dp++ = (x >> 7) | 128;
1184 xd->len = dp - xd->buf;
1188 data_addideof(struct extdata *xd, Id x, int eof)
1191 x = (x & 63) | ((x & ~63) << 1);
1192 data_addid(xd, (eof ? x: x | 64));
1196 data_addblob(struct extdata *xd, unsigned char *blob, int len)
1198 xd->buf = sat_extend(xd->buf, xd->len, len, 1, EXTDATA_BLOCK);
1199 memcpy(xd->buf + xd->len, blob, len);
1203 /*********************************/
1206 addschema_prepare(Repodata *data, Id *schematacache)
1211 memset(schematacache, 0, 256 * sizeof(Id));
1212 for (i = 0; i < data->nschemata; i++)
1214 for (sp = data->schemadata + data->schemata[i], h = 0; *sp; len++)
1217 schematacache[h] = i + 1;
1219 data->schemadata = sat_extend_resize(data->schemadata, data->schemadatalen, sizeof(Id), SCHEMATADATA_BLOCK);
1220 data->schemata = sat_extend_resize(data->schemata, data->nschemata, sizeof(Id), SCHEMATA_BLOCK);
1224 addschema(Repodata *data, Id *schema, Id *schematacache)
1229 for (sp = schema, len = 0, h = 0; *sp; len++)
1234 cid = schematacache[h];
1238 if (!memcmp(data->schemadata + data->schemata[cid], schema, len * sizeof(Id)))
1240 /* cache conflict */
1241 for (cid = 0; cid < data->nschemata; cid++)
1242 if (!memcmp(data->schemadata + data->schemata[cid], schema, len * sizeof(Id)))
1245 /* a new one. make room. */
1246 data->schemadata = sat_extend(data->schemadata, data->schemadatalen, len, sizeof(Id), SCHEMATADATA_BLOCK);
1247 data->schemata = sat_extend(data->schemata, data->nschemata, 1, sizeof(Id), SCHEMATA_BLOCK);
1249 memcpy(data->schemadata + data->schemadatalen, schema, len * sizeof(Id));
1250 data->schemata[data->nschemata] = data->schemadatalen;
1251 data->schemadatalen += len;
1252 schematacache[h] = data->nschemata + 1;
1254 fprintf(stderr, "addschema: new schema\n");
1256 return data->nschemata++;
1261 repodata_internalize(Repodata *data)
1264 Id id, entry, nentry, *ida;
1265 Id schematacache[256];
1266 Id schemaid, *schema, *sp, oldschema, *keyp, *seen;
1267 unsigned char *dp, *ndp;
1268 int newschema, oldcount;
1269 struct extdata newincore;
1270 struct extdata newvincore;
1275 newvincore.buf = data->vincore;
1276 newvincore.len = data->vincorelen;
1278 schema = sat_malloc2(data->nkeys, sizeof(Id));
1279 seen = sat_malloc2(data->nkeys, sizeof(Id));
1281 /* Merge the data already existing (in data->schemata, ->incoredata and
1282 friends) with the new attributes in data->attrs[]. */
1283 nentry = data->end - data->start;
1284 addschema_prepare(data, schematacache);
1285 memset(&newincore, 0, sizeof(newincore));
1286 data_addid(&newincore, 0);
1287 for (entry = 0; entry < nentry; entry++)
1289 memset(seen, 0, data->nkeys * sizeof(Id));
1291 dp = data->incoredata + data->incoreoffset[entry];
1292 if (data->incoredata)
1293 dp = data_read_id(dp, &oldschema);
1297 fprintf(stderr, "oldschema %d\n", oldschema);
1298 fprintf(stderr, "schemata %d\n", data->schemata[oldschema]);
1299 fprintf(stderr, "schemadata %p\n", data->schemadata);
1301 /* seen: -1: old data 0: skipped >0: id + 1 */
1304 for (keyp = data->schemadata + data->schemata[oldschema]; *keyp; keyp++)
1308 fprintf(stderr, "Inconsistent old data (key occured twice).\n");
1315 if (data->attrs[entry])
1316 for (keyp = data->attrs[entry]; *keyp; keyp += 2)
1323 seen[*keyp] = keyp[1] + 1;
1327 /* Ideally we'd like to sort the new schema here, to ensure
1328 schema equality independend of the ordering. We can't do that
1329 yet. For once see below (old ids need to come before new ids).
1330 An additional difficulty is that we also need to move
1331 the values with the keys. */
1332 schemaid = addschema(data, schema, schematacache);
1334 schemaid = oldschema;
1337 /* Now create data blob. We walk through the (possibly new) schema
1338 and either copy over old data, or insert the new. */
1339 /* XXX Here we rely on the fact that the (new) schema has the form
1340 o1 o2 o3 o4 ... | n1 n2 n3 ...
1341 (oX being the old keyids (possibly overwritten), and nX being
1342 the new keyids). This rules out sorting the keyids in order
1343 to ensure a small schema count. */
1344 data->incoreoffset[entry] = newincore.len;
1345 data_addid(&newincore, schemaid);
1346 for (keyp = data->schemadata + data->schemata[schemaid]; *keyp; keyp++)
1348 key = data->keys + *keyp;
1352 /* Skip the data associated with this old key. */
1353 if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
1355 ndp = data_skip(dp, REPOKEY_TYPE_ID);
1356 ndp = data_skip(ndp, REPOKEY_TYPE_ID);
1358 else if (key->storage == KEY_STORAGE_INCORE)
1359 ndp = data_skip(dp, key->type);
1362 if (seen[*keyp] == -1)
1364 /* If this key was an old one _and_ was not overwritten with
1365 a different value copy over the old value (we skipped it
1368 data_addblob(&newincore, dp, ndp - dp);
1371 else if (seen[*keyp])
1373 /* Otherwise we have a new value. Parse it into the internal
1376 unsigned int oldvincorelen = 0;
1379 if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
1382 oldvincorelen = xd->len;
1384 id = seen[*keyp] - 1;
1387 case REPOKEY_TYPE_VOID:
1388 case REPOKEY_TYPE_CONSTANT:
1389 case REPOKEY_TYPE_CONSTANTID:
1391 case REPOKEY_TYPE_STR:
1392 data_addblob(xd, data->attrdata + id, strlen((char *)(data->attrdata + id)) + 1);
1394 case REPOKEY_TYPE_ID:
1395 case REPOKEY_TYPE_NUM:
1396 case REPOKEY_TYPE_DIR:
1399 case REPOKEY_TYPE_IDARRAY:
1400 for (ida = data->attriddata + id; *ida; ida++)
1401 data_addideof(xd, ida[0], ida[1] ? 0 : 1);
1403 case REPOKEY_TYPE_DIRNUMNUMARRAY:
1404 for (ida = data->attriddata + id; *ida; ida += 3)
1406 data_addid(xd, ida[0]);
1407 data_addid(xd, ida[1]);
1408 data_addideof(xd, ida[2], ida[3] ? 0 : 1);
1411 case REPOKEY_TYPE_DIRSTRARRAY:
1412 for (ida = data->attriddata + id; *ida; ida += 2)
1414 data_addideof(xd, ida[0], ida[2] ? 0 : 1);
1415 data_addblob(xd, data->attrdata + ida[1], strlen((char *)(data->attrdata + ida[1])) + 1);
1419 fprintf(stderr, "don't know how to handle type %d\n", key->type);
1422 if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
1424 /* put offset/len in incore */
1425 data_addid(&newincore, data->lastverticaloffset + oldvincorelen);
1426 oldvincorelen = xd->len - oldvincorelen;
1427 data_addid(&newincore, oldvincorelen);
1432 if (data->attrs[entry])
1433 sat_free(data->attrs[entry]);
1438 sat_free(data->incoredata);
1439 data->incoredata = newincore.buf;
1440 data->incoredatalen = newincore.len;
1441 data->incoredatafree = 0;
1443 sat_free(data->vincore);
1444 data->vincore = newvincore.buf;
1445 data->vincorelen = newvincore.len;
1447 data->attrs = sat_free(data->attrs);
1448 data->attrdata = sat_free(data->attrdata);
1449 data->attriddata = sat_free(data->attriddata);
1450 data->attrdatalen = 0;
1451 data->attriddatalen = 0;
1455 repodata_str2dir(Repodata *data, const char *dir, int create)
1461 while (*dir == '/' && dir[1] == '/')
1463 if (*dir == '/' && !dir[1])
1467 dire = strchrnul(dir, '/');
1468 if (data->localpool)
1469 id = stringpool_strn2id(&data->spool, dir, dire - dir, create);
1471 id = strn2id(data->repo->pool, dir, dire - dir, create);
1474 parent = dirpool_add_dir(&data->dirpool, parent, id, create);
1487 repodata_dir2str(Repodata *data, Id did, const char *suf)
1489 Pool *pool = data->repo->pool;
1496 return suf ? suf : "";
1500 comp = dirpool_compid(&data->dirpool, parent);
1501 comps = stringpool_id2str(data->localpool ? &data->spool : &pool->ss, comp);
1503 parent = dirpool_parent(&data->dirpool, parent);
1508 l += strlen(suf) + 1;
1509 p = pool_alloctmpspace(pool, l + 1) + l;
1520 comp = dirpool_compid(&data->dirpool, parent);
1521 comps = stringpool_id2str(data->localpool ? &data->spool : &pool->ss, comp);
1524 strncpy(p, comps, l);
1525 parent = dirpool_parent(&data->dirpool, parent);
1533 repodata_compress_page(unsigned char *page, unsigned int len, unsigned char *cpage, unsigned int max)
1535 return compress_buf(page, len, cpage, max);
1538 #define SOLV_ERROR_EOF 3
1540 static inline unsigned int
1546 for (i = 0; i < 4; i++)
1556 #define SOLV_ERROR_EOF 3
1557 #define SOLV_ERROR_CORRUPT 6
1559 /* Try to either setup on-demand paging (using FP as backing
1560 file), or in case that doesn't work (FP not seekable) slurps in
1561 all pages and deactivates paging. */
1563 repodata_read_or_setup_pages(Repodata *data, unsigned int pagesz, unsigned int blobsz)
1565 FILE *fp = data->fp;
1566 unsigned int npages;
1568 unsigned int can_seek;
1570 unsigned char buf[BLOB_PAGESIZE];
1572 if (pagesz != BLOB_PAGESIZE)
1574 /* We could handle this by slurping in everything. */
1575 data->error = SOLV_ERROR_CORRUPT;
1579 if ((cur_file_ofs = ftell(fp)) < 0)
1583 data->pagefd = dup(fileno(fp));
1584 if (data->pagefd == -1)
1588 fprintf (stderr, "can %sseek\n", can_seek ? "" : "NOT ");
1590 npages = (blobsz + BLOB_PAGESIZE - 1) / BLOB_PAGESIZE;
1592 data->num_pages = npages;
1593 data->pages = sat_malloc2(npages, sizeof(data->pages[0]));
1595 /* If we can't seek on our input we have to slurp in everything. */
1597 data->blob_store = sat_malloc(npages * BLOB_PAGESIZE);
1598 for (i = 0; i < npages; i++)
1600 unsigned int in_len = read_u32(fp);
1601 unsigned int compressed = in_len & 1;
1602 Attrblobpage *p = data->pages + i;
1605 fprintf (stderr, "page %d: len %d (%scompressed)\n",
1606 i, in_len, compressed ? "" : "not ");
1612 p->file_offset = cur_file_ofs;
1613 p->file_size = in_len * 2 + compressed;
1614 if (fseek(fp, in_len, SEEK_CUR) < 0)
1617 fprintf (stderr, "can't seek after we thought we can\n");
1618 /* We can't fall back to non-seeking behaviour as we already
1619 read over some data pages without storing them away. */
1620 data->error = SOLV_ERROR_EOF;
1621 close(data->pagefd);
1625 cur_file_ofs += in_len;
1629 unsigned int out_len;
1630 void *dest = data->blob_store + i * BLOB_PAGESIZE;
1631 p->mapped_at = i * BLOB_PAGESIZE;
1634 /* We can't seek, so suck everything in. */
1635 if (fread(compressed ? buf : dest, in_len, 1, fp) != 1)
1638 data->error = SOLV_ERROR_EOF;
1643 out_len = unchecked_decompress_buf(buf, in_len, dest, BLOB_PAGESIZE);
1644 if (out_len != BLOB_PAGESIZE && i < npages - 1)
1646 data->error = SOLV_ERROR_CORRUPT;
1655 repodata_disable_paging(Repodata *data)
1657 if (maybe_load_repodata(data, 0)
1659 load_page_range (data, 0, data->num_pages - 1);
1662 vim:cinoptions={.5s,g0,p5,t0,(0,^-0.5s,n-0.5s:tw=78:cindent:sw=4: