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
41 repodata_free(Repodata *data)
44 sat_free(data->schemata);
45 sat_free(data->schemadata);
47 sat_free(data->spool.strings);
48 sat_free(data->spool.stringspace);
49 sat_free(data->spool.stringhashtbl);
51 sat_free(data->dirpool.dirs);
52 sat_free(data->dirpool.dirtraverse);
54 sat_free(data->incoredata);
55 sat_free(data->incoreoffset);
56 sat_free(data->verticaloffset);
58 sat_free(data->blob_store);
59 sat_free(data->pages);
60 sat_free(data->mapped);
62 sat_free(data->vincore);
64 sat_free(data->attrs);
65 sat_free(data->attrdata);
66 sat_free(data->attriddata);
68 if (data->pagefd != -1)
72 static unsigned char *
73 forward_to_key(Repodata *data, Id key, Id schemaid, unsigned char *dp)
77 keyp = data->schemadata + data->schemata[schemaid];
78 while ((k = *keyp++) != 0)
82 if (data->keys[k].storage == KEY_STORAGE_VERTICAL_OFFSET)
84 dp = data_skip(dp, REPOKEY_TYPE_ID); /* skip that offset */
85 dp = data_skip(dp, REPOKEY_TYPE_ID); /* skip that length */
88 if (data->keys[k].storage != KEY_STORAGE_INCORE)
90 dp = data_skip(dp, data->keys[k].type);
95 #define BLOB_PAGEBITS 15
96 #define BLOB_PAGESIZE (1 << BLOB_PAGEBITS)
98 static unsigned char *
99 load_page_range(Repodata *data, unsigned int pstart, unsigned int pend)
101 /* Make sure all pages from PSTART to PEND (inclusive) are loaded,
102 and are consecutive. Return a pointer to the mapping of PSTART. */
103 unsigned char buf[BLOB_PAGESIZE];
106 /* Quick check in case all pages are there already and consecutive. */
107 for (i = pstart; i <= pend; i++)
108 if (data->pages[i].mapped_at == -1
110 && data->pages[i].mapped_at
111 != data->pages[i-1].mapped_at + BLOB_PAGESIZE))
114 return data->blob_store + data->pages[pstart].mapped_at;
116 /* Ensure that we can map the numbers of pages we need at all. */
117 if (pend - pstart + 1 > data->ncanmap)
119 unsigned int oldcan = data->ncanmap;
120 data->ncanmap = pend - pstart + 1;
121 if (data->ncanmap < 4)
123 data->mapped = sat_realloc2(data->mapped, data->ncanmap, sizeof(data->mapped[0]));
124 memset (data->mapped + oldcan, 0, (data->ncanmap - oldcan) * sizeof (data->mapped[0]));
125 data->blob_store = sat_realloc2(data->blob_store, data->ncanmap, BLOB_PAGESIZE);
127 fprintf (stderr, "PAGE: can map %d pages\n", data->ncanmap);
131 /* Now search for "cheap" space in our store. Space is cheap if it's either
132 free (very cheap) or contains pages we search for anyway. */
134 /* Setup cost array. */
135 unsigned int cost[data->ncanmap];
136 for (i = 0; i < data->ncanmap; i++)
138 unsigned int pnum = data->mapped[i];
144 Attrblobpage *p = data->pages + pnum;
145 assert (p->mapped_at != -1);
146 if (pnum >= pstart && pnum <= pend)
153 /* And search for cheapest space. */
154 unsigned int best_cost = -1;
155 unsigned int best = 0;
156 unsigned int same_cost = 0;
157 for (i = 0; i + pend - pstart < data->ncanmap; i++)
159 unsigned int c = cost[i];
161 for (j = 0; j < pend - pstart + 1; j++)
164 best_cost = c, best = i;
165 else if (c == best_cost)
167 /* A null cost won't become better. */
171 /* If all places have the same cost we would thrash on slot 0. Avoid
172 this by doing a round-robin strategy in this case. */
173 if (same_cost == data->ncanmap - pend + pstart - 1)
174 best = data->rr_counter++ % (data->ncanmap - pend + pstart);
176 /* So we want to map our pages from [best] to [best+pend-pstart].
177 Use a very simple strategy, which doesn't make the best use of
178 our resources, but works. Throw away all pages in that range
179 (even ours) then copy around ours (in case they were outside the
180 range) or read them in. */
181 for (i = best; i < best + pend - pstart + 1; i++)
183 unsigned int pnum = data->mapped[i];
185 /* If this page is exactly at the right place already,
186 no need to evict it. */
187 && pnum != pstart + i - best)
189 /* Evict this page. */
191 fprintf (stderr, "PAGE: evict page %d from %d\n", pnum, i);
195 data->pages[pnum].mapped_at = -1;
199 /* Everything is free now. Read in the pages we want. */
200 for (i = pstart; i <= pend; i++)
202 Attrblobpage *p = data->pages + i;
203 unsigned int pnum = i - pstart + best;
204 void *dest = data->blob_store + pnum * BLOB_PAGESIZE;
205 if (p->mapped_at != -1)
207 if (p->mapped_at != pnum * BLOB_PAGESIZE)
210 fprintf (stderr, "PAGECOPY: %d to %d\n", i, pnum);
212 /* Still mapped somewhere else, so just copy it from there. */
213 memcpy (dest, data->blob_store + p->mapped_at, BLOB_PAGESIZE);
214 data->mapped[p->mapped_at / BLOB_PAGESIZE] = 0;
219 unsigned int in_len = p->file_size;
220 unsigned int compressed = in_len & 1;
223 fprintf (stderr, "PAGEIN: %d to %d", i, pnum);
225 if (pread(data->pagefd, compressed ? buf : dest, in_len, p->file_offset) != in_len)
227 perror ("mapping pread");
232 unsigned int out_len;
233 out_len = unchecked_decompress_buf(buf, in_len,
234 dest, BLOB_PAGESIZE);
235 if (out_len != BLOB_PAGESIZE && i < data->num_pages - 1)
237 fprintf(stderr, "can't decompress\n");
241 fprintf (stderr, " (expand %d to %d)", in_len, out_len);
245 fprintf (stderr, "\n");
248 p->mapped_at = pnum * BLOB_PAGESIZE;
249 data->mapped[pnum] = i + 1;
251 return data->blob_store + best * BLOB_PAGESIZE;
254 static unsigned char *
255 make_vertical_available(Repodata *data, Repokey *key, Id off, Id len)
258 if (key->type == REPOKEY_TYPE_VOID)
260 if (off >= data->lastverticaloffset)
262 off -= data->lastverticaloffset;
263 if (off + len > data->vincorelen)
265 return data->vincore + off;
267 if (data->pagefd == -1)
269 if (off + len > key->size)
271 /* we now have the offset, go into vertical */
272 off += data->verticaloffset[key - data->keys];
273 dp = load_page_range(data, off / BLOB_PAGESIZE, (off + len - 1) / BLOB_PAGESIZE);
275 dp += off % BLOB_PAGESIZE;
279 static inline unsigned char *
280 get_data(Repodata *data, Repokey *key, unsigned char **dpp)
282 unsigned char *dp = *dpp;
286 if (key->storage == KEY_STORAGE_INCORE)
288 /* hmm, this is a bit expensive */
289 *dpp = data_skip(dp, key->type);
292 else if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
295 dp = data_read_id(dp, &off);
296 dp = data_read_id(dp, &len);
298 return make_vertical_available(data, key, off, len);
304 maybe_load_repodata(Repodata *data)
306 if (data->state == REPODATA_STUB)
308 if (data->loadcallback)
309 data->loadcallback(data);
311 data->state = REPODATA_ERROR;
313 if (data->state == REPODATA_AVAILABLE)
315 data->state = REPODATA_ERROR;
320 repodata_lookup_str(Repodata *data, Id entry, Id keyid)
327 if (!maybe_load_repodata (data))
330 dp = data->incoredata + data->incoreoffset[entry];
331 dp = data_read_id(dp, &schema);
332 /* make sure the schema of this solvable contains the key */
333 for (keyp = data->schemadata + data->schemata[schema]; *keyp != keyid; keyp++)
336 dp = forward_to_key(data, keyid, schema, dp);
337 key = data->keys + keyid;
338 dp = get_data(data, key, &dp);
341 if (key->type == REPOKEY_TYPE_STR)
342 return (const char *)dp;
343 if (key->type == REPOKEY_TYPE_CONSTANTID)
344 return id2str(data->repo->pool, key->size);
345 if (key->type == REPOKEY_TYPE_ID)
346 dp = data_read_id(dp, &id);
350 return data->spool.stringspace + data->spool.strings[id];
351 return id2str(data->repo->pool, id);
355 repodata_lookup_num(Repodata *data, Id entry, Id keyid, unsigned *value)
365 if (!maybe_load_repodata (data))
368 dp = data->incoredata + data->incoreoffset[entry];
369 dp = data_read_id(dp, &schema);
370 /* make sure the schema of this solvable contains the key */
371 for (keyp = data->schemadata + data->schemata[schema]; *keyp != keyid; keyp++)
374 dp = forward_to_key(data, keyid, schema, dp);
375 key = data->keys + keyid;
376 dp = get_data(data, key, &dp);
379 if (key->type == REPOKEY_TYPE_NUM
380 || key->type == REPOKEY_TYPE_U32
381 || key->type == REPOKEY_TYPE_CONSTANT)
383 dp = data_fetch(dp, &kv, key);
391 repodata_search(Repodata *data, Id entry, Id keyname, int (*callback)(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv), void *cbdata)
395 Id k, keyid, *kp, *keyp;
396 unsigned char *dp, *ddp;
401 if (!maybe_load_repodata (data))
404 dp = data->incoredata + data->incoreoffset[entry];
405 dp = data_read_id(dp, &schema);
406 keyp = data->schemadata + data->schemata[schema];
409 /* search in a specific key */
410 for (kp = keyp; (k = *kp++) != 0; )
411 if (data->keys[k].name == keyname)
415 dp = forward_to_key(data, k, schema, dp);
421 while ((keyid = *keyp++) != 0)
424 key = data->keys + keyid;
425 ddp = get_data(data, key, &dp);
428 ddp = data_fetch(ddp, &kv, key);
431 stop = callback(cbdata, data->repo->pool->solvables + data->start + entry, data, key, &kv);
433 while (!kv.eof && !stop);
434 if (onekey || stop > SEARCH_NEXT_KEY)
440 dataiterator_newdata(Dataiterator *di)
442 Id keyname = di->keyname;
443 Repodata *data = di->data;
446 if (data->state == REPODATA_STUB)
451 for (j = 1; j < data->nkeys; j++)
452 if (keyname == data->keys[j].name)
454 if (j == data->nkeys)
458 if (data->loadcallback)
459 data->loadcallback(data);
461 data->state = REPODATA_ERROR;
463 if (data->state == REPODATA_ERROR)
467 unsigned char *dp = data->incoredata + data->incoreoffset[di->solvid - data->start];
468 dp = data_read_id(dp, &schema);
469 Id *keyp = data->schemadata + data->schemata[schema];
473 /* search in a specific key */
474 for (kp = keyp; (k = *kp++) != 0; )
475 if (data->keys[k].name == keyname)
479 dp = forward_to_key(data, k, schema, dp);
489 di->key = di->data->keys + keyid;
494 di->dp = get_data(di->data, di->key, &di->nextkeydp);
499 dataiterator_init(Dataiterator *di, Repo *repo, Id p, Id keyname,
500 const char *match, int flags)
506 di->flags |= __SEARCH_ONESOLVABLE;
507 di->data = repo->repodata - 1;
508 if (flags & SEARCH_NO_STORAGE_SOLVABLE)
515 di->solvid = repo->start - 1;
516 di->data = repo->repodata + repo->nrepodata - 1;
520 di->keyname = keyname;
521 static Id zeroid = 0;
528 /* FIXME factor and merge with repo_matchvalue */
530 dataiterator_match(Dataiterator *di, KeyValue *kv)
532 int flags = di->flags;
534 if ((flags & SEARCH_STRINGMASK) != 0)
536 switch (di->key->type)
538 case REPOKEY_TYPE_ID:
539 case REPOKEY_TYPE_IDARRAY:
540 if (di->data && di->data->localpool)
541 kv->str = stringpool_id2str(&di->data->spool, kv->id);
543 kv->str = id2str(di->repo->pool, kv->id);
545 case REPOKEY_TYPE_STR:
550 switch ((flags & SEARCH_STRINGMASK))
552 case SEARCH_SUBSTRING:
553 if (flags & SEARCH_NOCASE)
555 if (!strcasestr(kv->str, di->match))
560 if (!strstr(kv->str, di->match))
565 if (flags & SEARCH_NOCASE)
567 if (strcasecmp(di->match, kv->str))
572 if (strcmp(di->match, kv->str))
577 if (fnmatch(di->match, kv->str, (flags & SEARCH_NOCASE) ? FNM_CASEFOLD : 0))
582 if (regexec(&di->regexp, kv->str, 0, NULL, 0))
592 static Repokey solvablekeys[RPM_RPMDBID - SOLVABLE_NAME + 1] = {
593 { SOLVABLE_NAME, REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
594 { SOLVABLE_ARCH, REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
595 { SOLVABLE_EVR, REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
596 { SOLVABLE_VENDOR, REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
597 { SOLVABLE_PROVIDES, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
598 { SOLVABLE_OBSOLETES, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
599 { SOLVABLE_CONFLICTS, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
600 { SOLVABLE_REQUIRES, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
601 { SOLVABLE_RECOMMENDS, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
602 { SOLVABLE_SUGGESTS, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
603 { SOLVABLE_SUPPLEMENTS, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
604 { SOLVABLE_ENHANCES, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
605 { SOLVABLE_FRESHENS, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
606 { RPM_RPMDBID, REPOKEY_TYPE_U32, 0, KEY_STORAGE_SOLVABLE },
610 dataiterator_step(Dataiterator *di)
624 di->kv.eof = idp[1] ? 0 : 1;
630 Solvable *s = di->repo->pool->solvables + di->solvid;
631 int state = di->state;
632 di->key = solvablekeys + state - 1;
634 di->state = RPM_RPMDBID;
641 state = di->keyname - 1;
660 case SOLVABLE_VENDOR:
663 di->kv.id = s->vendor;
665 case SOLVABLE_PROVIDES:
666 di->idp = s->provides
667 ? di->repo->idarraydata + s->provides : 0;
669 case SOLVABLE_OBSOLETES:
670 di->idp = s->obsoletes
671 ? di->repo->idarraydata + s->obsoletes : 0;
673 case SOLVABLE_CONFLICTS:
674 di->idp = s->conflicts
675 ? di->repo->idarraydata + s->conflicts : 0;
677 case SOLVABLE_REQUIRES:
678 di->idp = s->requires
679 ? di->repo->idarraydata + s->requires : 0;
681 case SOLVABLE_RECOMMENDS:
682 di->idp = s->recommends
683 ? di->repo->idarraydata + s->recommends : 0;
685 case SOLVABLE_SUPPLEMENTS:
686 di->idp = s->supplements
687 ? di->repo->idarraydata + s->supplements : 0;
689 case SOLVABLE_SUGGESTS:
690 di->idp = s->suggests
691 ? di->repo->idarraydata + s->suggests : 0;
693 case SOLVABLE_ENHANCES:
694 di->idp = s->enhances
695 ? di->repo->idarraydata + s->enhances : 0;
697 case SOLVABLE_FRESHENS:
698 di->idp = s->freshens
699 ? di->repo->idarraydata + s->freshens : 0;
702 if (!di->repo->rpmdbid)
704 di->kv.num = di->repo->rpmdbid[di->solvid - di->repo->start];
707 di->data = di->repo->repodata - 1;
718 di->dp = data_fetch(di->dp, &di->kv, di->key);
723 if (di->keyname || !(keyid = *di->keyp++))
727 Repo *repo = di->repo;
728 Repodata *data = ++di->data;
729 if (data >= repo->repodata + repo->nrepodata)
731 if (di->flags & __SEARCH_ONESOLVABLE)
733 while (++di->solvid < repo->end)
734 if (repo->pool->solvables[di->solvid].repo == repo)
736 if (di->solvid >= repo->end)
738 di->data = repo->repodata - 1;
739 if (di->flags & SEARCH_NO_STORAGE_SOLVABLE)
741 static Id zeroid = 0;
746 if (di->solvid >= data->start && di->solvid < data->end)
748 dataiterator_newdata(di);
756 di->key = di->data->keys + keyid;
757 di->dp = get_data(di->data, di->key, &di->nextkeydp);
759 di->dp = data_fetch(di->dp, &di->kv, di->key);
764 || dataiterator_match(di, &di->kv))
771 repodata_init(Repodata *data, Repo *repo, int localpool)
773 memset(data, 0, sizeof (*data));
775 data->localpool = localpool;
777 stringpool_init_empty(&data->spool);
778 data->keys = sat_calloc(1, sizeof(Repokey));
780 data->schemata = sat_calloc(1, sizeof(Id));
781 data->schemadata = sat_calloc(1, sizeof(Id));
783 data->schemadatalen = 1;
784 data->start = repo->start;
785 data->end = repo->end;
786 data->incoreoffset = sat_extend_resize(0, data->end - data->start, sizeof(Id), REPODATA_BLOCK);
790 /* extend repodata so that it includes solvables p */
792 repodata_extend(Repodata *data, Id p)
794 if (data->start == data->end)
795 data->start = data->end = p;
798 int old = data->end - data->start;
799 int new = p - data->end + 1;
802 data->attrs = sat_extend(data->attrs, old, new, sizeof(Id *), REPODATA_BLOCK);
803 memset(data->attrs + old, 0, new * sizeof(Id *));
805 data->incoreoffset = sat_extend(data->incoreoffset, old, new, sizeof(Id), REPODATA_BLOCK);
806 memset(data->incoreoffset + old, 0, new * sizeof(Id));
811 int old = data->end - data->start;
812 int new = data->start - p;
815 data->attrs = sat_extend_resize(data->attrs, old + new, sizeof(Id *), REPODATA_BLOCK);
816 memmove(data->attrs + new, data->attrs, old * sizeof(Id *));
817 memset(data->attrs, 0, new * sizeof(Id *));
819 data->incoreoffset = sat_extend_resize(data->incoreoffset, old + new, sizeof(Id), REPODATA_BLOCK);
820 memmove(data->incoreoffset + new, data->incoreoffset, old * sizeof(Id));
821 memset(data->incoreoffset, 0, new * sizeof(Id));
827 repodata_extend_block(Repodata *data, Id start, Id num)
831 if (!data->incoreoffset)
833 data->incoreoffset = sat_extend_resize(data->incoreoffset, num, sizeof(Id), REPODATA_BLOCK);
834 memset(data->incoreoffset, 0, num * sizeof(Id));
836 data->end = start + num;
839 repodata_extend(data, start);
841 repodata_extend(data, start + num - 1);
844 #define REPODATA_ATTRS_BLOCK 63
845 #define REPODATA_ATTRDATA_BLOCK 1023
846 #define REPODATA_ATTRIDDATA_BLOCK 63
849 repodata_insert_keyid(Repodata *data, Id entry, Id keyid, Id val, int overwrite)
855 data->attrs = sat_extend_resize(0, data->end - data->start, sizeof(Id *), REPODATA_BLOCK);
856 memset(data->attrs, 0, (data->end - data->start) * sizeof(Id *));
859 if (data->attrs[entry])
861 for (pp = data->attrs[entry]; *pp; pp += 2)
862 /* Determine equality based on the name only, allows us to change
863 type (when overwrite is set), and makes TYPE_CONSTANT work. */
864 if (data->keys[*pp].name == data->keys[keyid].name)
875 i = pp - data->attrs[entry];
877 data->attrs[entry] = sat_extend(data->attrs[entry], i, 3, sizeof(Id), REPODATA_ATTRS_BLOCK);
878 pp = data->attrs[entry] + i;
885 repodata_set(Repodata *data, Id entry, Repokey *key, Id val)
889 /* find key in keys */
890 for (keyid = 1; keyid < data->nkeys; keyid++)
891 if (data->keys[keyid].name == key->name && data->keys[keyid].type == key->type)
893 if ((key->type == REPOKEY_TYPE_CONSTANT || key->type == REPOKEY_TYPE_CONSTANTID) && key->size != data->keys[keyid].size)
897 if (keyid == data->nkeys)
899 /* allocate new key */
900 data->keys = sat_realloc2(data->keys, data->nkeys + 1, sizeof(Repokey));
901 data->keys[data->nkeys++] = *key;
902 if (data->verticaloffset)
904 data->verticaloffset = sat_realloc2(data->verticaloffset, data->nkeys, sizeof(Id));
905 data->verticaloffset[data->nkeys - 1] = 0;
908 repodata_insert_keyid(data, entry, keyid, val, 1);
912 repodata_set_id(Repodata *data, Id entry, Id keyname, Id id)
916 key.type = REPOKEY_TYPE_ID;
918 key.storage = KEY_STORAGE_INCORE;
919 repodata_set(data, entry, &key, id);
923 repodata_set_num(Repodata *data, Id entry, Id keyname, Id num)
927 key.type = REPOKEY_TYPE_NUM;
929 key.storage = KEY_STORAGE_INCORE;
930 repodata_set(data, entry, &key, num);
934 repodata_set_poolstr(Repodata *data, Id entry, Id keyname, const char *str)
939 id = stringpool_str2id(&data->spool, str, 1);
941 id = str2id(data->repo->pool, str, 1);
943 key.type = REPOKEY_TYPE_ID;
945 key.storage = KEY_STORAGE_INCORE;
946 repodata_set(data, entry, &key, id);
950 repodata_set_constant(Repodata *data, Id entry, Id keyname, Id constant)
954 key.type = REPOKEY_TYPE_CONSTANT;
956 key.storage = KEY_STORAGE_INCORE;
957 repodata_set(data, entry, &key, 0);
961 repodata_set_constantid(Repodata *data, Id entry, Id keyname, Id id)
965 key.type = REPOKEY_TYPE_CONSTANTID;
967 key.storage = KEY_STORAGE_INCORE;
968 repodata_set(data, entry, &key, 0);
972 repodata_set_void(Repodata *data, Id entry, Id keyname)
976 key.type = REPOKEY_TYPE_VOID;
978 key.storage = KEY_STORAGE_INCORE;
979 repodata_set(data, entry, &key, 0);
983 repodata_set_str(Repodata *data, Id entry, Id keyname, const char *str)
990 key.type = REPOKEY_TYPE_STR;
992 key.storage = KEY_STORAGE_INCORE;
993 data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
994 memcpy(data->attrdata + data->attrdatalen, str, l);
995 repodata_set(data, entry, &key, data->attrdatalen);
996 data->attrdatalen += l;
1000 repodata_add_dirnumnum(Repodata *data, Id entry, Id keyname, Id dir, Id num, Id num2)
1006 fprintf(stderr, "repodata_add_dirnumnum %d %d %d %d (%d)\n", entry, dir, num, num2, data->attriddatalen);
1008 if (data->attrs && data->attrs[entry])
1010 for (pp = data->attrs[entry]; *pp; pp += 2)
1011 if (data->keys[*pp].name == keyname && data->keys[*pp].type == REPOKEY_TYPE_DIRNUMNUMARRAY)
1016 for (ida = data->attriddata + pp[1]; *ida; ida += 3)
1018 if (ida + 1 == data->attriddata + data->attriddatalen)
1020 /* this was the last entry, just append it */
1021 data->attriddata = sat_extend(data->attriddata, data->attriddatalen, 3, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1022 data->attriddatalen--; /* overwrite terminating 0 */
1026 /* too bad. move to back. */
1027 data->attriddata = sat_extend(data->attriddata, data->attriddatalen, oldsize + 4, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1028 memcpy(data->attriddata + data->attriddatalen, data->attriddata + pp[1], oldsize * sizeof(Id));
1029 pp[1] = data->attriddatalen;
1030 data->attriddatalen += oldsize;
1032 data->attriddata[data->attriddatalen++] = dir;
1033 data->attriddata[data->attriddatalen++] = num;
1034 data->attriddata[data->attriddatalen++] = num2;
1035 data->attriddata[data->attriddatalen++] = 0;
1040 key.type = REPOKEY_TYPE_DIRNUMNUMARRAY;
1042 key.storage = KEY_STORAGE_INCORE;
1043 data->attriddata = sat_extend(data->attriddata, data->attriddatalen, 4, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1044 repodata_set(data, entry, &key, data->attriddatalen);
1045 data->attriddata[data->attriddatalen++] = dir;
1046 data->attriddata[data->attriddatalen++] = num;
1047 data->attriddata[data->attriddatalen++] = num2;
1048 data->attriddata[data->attriddatalen++] = 0;
1052 repodata_add_dirstr(Repodata *data, Id entry, Id keyname, Id dir, const char *str)
1054 Id *ida, *pp, stroff;
1058 l = strlen(str) + 1;
1059 data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
1060 memcpy(data->attrdata + data->attrdatalen, str, l);
1061 stroff = data->attrdatalen;
1062 data->attrdatalen += l;
1065 fprintf(stderr, "repodata_add_dirstr %d %d %s (%d)\n", entry, dir, str, data->attriddatalen);
1067 if (data->attrs && data->attrs[entry])
1069 for (pp = data->attrs[entry]; *pp; pp += 2)
1070 if (data->keys[*pp].name == keyname && data->keys[*pp].type == REPOKEY_TYPE_DIRSTRARRAY)
1075 for (ida = data->attriddata + pp[1]; *ida; ida += 2)
1077 if (ida + 1 == data->attriddata + data->attriddatalen)
1079 /* this was the last entry, just append it */
1080 data->attriddata = sat_extend(data->attriddata, data->attriddatalen, 2, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1081 data->attriddatalen--; /* overwrite terminating 0 */
1085 /* too bad. move to back. */
1086 data->attriddata = sat_extend(data->attriddata, data->attriddatalen, oldsize + 3, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1087 memcpy(data->attriddata + data->attriddatalen, data->attriddata + pp[1], oldsize * sizeof(Id));
1088 pp[1] = data->attriddatalen;
1089 data->attriddatalen += oldsize;
1091 data->attriddata[data->attriddatalen++] = dir;
1092 data->attriddata[data->attriddatalen++] = stroff;
1093 data->attriddata[data->attriddatalen++] = 0;
1098 key.type = REPOKEY_TYPE_DIRSTRARRAY;
1100 key.storage = KEY_STORAGE_INCORE;
1101 data->attriddata = sat_extend(data->attriddata, data->attriddatalen, 3, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1102 repodata_set(data, entry, &key, data->attriddatalen);
1103 data->attriddata[data->attriddatalen++] = dir;
1104 data->attriddata[data->attriddatalen++] = stroff;
1105 data->attriddata[data->attriddatalen++] = 0;
1109 repodata_merge_attrs (Repodata *data, Id dest, Id src)
1112 for (keyp = data->attrs[src]; *keyp; keyp += 2)
1113 repodata_insert_keyid(data, dest, keyp[0], keyp[1], 0);
1116 /*********************************/
1118 /* unify with repo_write! */
1120 #define EXTDATA_BLOCK 1023
1121 #define SCHEMATA_BLOCK 31
1122 #define SCHEMATADATA_BLOCK 255
1130 data_addid(struct extdata *xd, Id x)
1133 xd->buf = sat_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
1134 dp = xd->buf + xd->len;
1139 *dp++ = (x >> 28) | 128;
1141 *dp++ = (x >> 21) | 128;
1142 *dp++ = (x >> 14) | 128;
1145 *dp++ = (x >> 7) | 128;
1147 xd->len = dp - xd->buf;
1151 data_addideof(struct extdata *xd, Id x, int eof)
1154 x = (x & 63) | ((x & ~63) << 1);
1155 data_addid(xd, (eof ? x: x | 64));
1159 data_addblob(struct extdata *xd, unsigned char *blob, int len)
1161 xd->buf = sat_extend(xd->buf, xd->len, len, 1, EXTDATA_BLOCK);
1162 memcpy(xd->buf + xd->len, blob, len);
1166 /*********************************/
1169 addschema_prepare(Repodata *data, Id *schematacache)
1174 memset(schematacache, 0, 256 * sizeof(Id));
1175 for (i = 0; i < data->nschemata; i++)
1177 for (sp = data->schemadata + data->schemata[i], h = 0; *sp; len++)
1180 schematacache[h] = i + 1;
1182 data->schemadata = sat_extend_resize(data->schemadata, data->schemadatalen, sizeof(Id), SCHEMATADATA_BLOCK);
1183 data->schemata = sat_extend_resize(data->schemata, data->nschemata, sizeof(Id), SCHEMATA_BLOCK);
1187 addschema(Repodata *data, Id *schema, Id *schematacache)
1192 for (sp = schema, len = 0, h = 0; *sp; len++)
1197 cid = schematacache[h];
1201 if (!memcmp(data->schemadata + data->schemata[cid], schema, len * sizeof(Id)))
1203 /* cache conflict */
1204 for (cid = 0; cid < data->nschemata; cid++)
1205 if (!memcmp(data->schemadata + data->schemata[cid], schema, len * sizeof(Id)))
1208 /* a new one. make room. */
1209 data->schemadata = sat_extend(data->schemadata, data->schemadatalen, len, sizeof(Id), SCHEMATADATA_BLOCK);
1210 data->schemata = sat_extend(data->schemata, data->nschemata, 1, sizeof(Id), SCHEMATA_BLOCK);
1212 memcpy(data->schemadata + data->schemadatalen, schema, len * sizeof(Id));
1213 data->schemata[data->nschemata] = data->schemadatalen;
1214 data->schemadatalen += len;
1215 schematacache[h] = data->nschemata + 1;
1217 fprintf(stderr, "addschema: new schema\n");
1219 return data->nschemata++;
1224 repodata_internalize(Repodata *data)
1227 Id id, entry, nentry, *ida;
1228 Id schematacache[256];
1229 Id schemaid, *schema, *sp, oldschema, *keyp, *seen;
1230 unsigned char *dp, *ndp;
1231 int newschema, oldcount;
1232 struct extdata newincore;
1233 struct extdata newvincore;
1238 newvincore.buf = data->vincore;
1239 newvincore.len = data->vincorelen;
1241 schema = sat_malloc2(data->nkeys, sizeof(Id));
1242 seen = sat_malloc2(data->nkeys, sizeof(Id));
1244 /* Merge the data already existing (in data->schemata, ->incoredata and
1245 friends) with the new attributes in data->attrs[]. */
1246 nentry = data->end - data->start;
1247 addschema_prepare(data, schematacache);
1248 memset(&newincore, 0, sizeof(newincore));
1249 for (entry = 0; entry < nentry; entry++)
1251 memset(seen, 0, data->nkeys * sizeof(Id));
1253 dp = data->incoredata + data->incoreoffset[entry];
1254 if (data->incoredata)
1255 dp = data_read_id(dp, &oldschema);
1259 fprintf(stderr, "oldschema %d\n", oldschema);
1260 fprintf(stderr, "schemata %d\n", data->schemata[oldschema]);
1261 fprintf(stderr, "schemadata %p\n", data->schemadata);
1263 /* seen: -1: old data 0: skipped >0: id + 1 */
1266 for (keyp = data->schemadata + data->schemata[oldschema]; *keyp; keyp++)
1270 fprintf(stderr, "Inconsistent old data (key occured twice).\n");
1277 if (data->attrs[entry])
1278 for (keyp = data->attrs[entry]; *keyp; keyp += 2)
1285 seen[*keyp] = keyp[1] + 1;
1289 /* Ideally we'd like to sort the new schema here, to ensure
1290 schema equality independend of the ordering. We can't do that
1291 yet. For once see below (old ids need to come before new ids).
1292 An additional difficulty is that we also need to move
1293 the values with the keys. */
1294 schemaid = addschema(data, schema, schematacache);
1296 schemaid = oldschema;
1299 /* Now create data blob. We walk through the (possibly new) schema
1300 and either copy over old data, or insert the new. */
1301 /* XXX Here we rely on the fact that the (new) schema has the form
1302 o1 o2 o3 o4 ... | n1 n2 n3 ...
1303 (oX being the old keyids (possibly overwritten), and nX being
1304 the new keyids). This rules out sorting the keyids in order
1305 to ensure a small schema count. */
1306 data->incoreoffset[entry] = newincore.len;
1307 data_addid(&newincore, schemaid);
1308 for (keyp = data->schemadata + data->schemata[schemaid]; *keyp; keyp++)
1310 key = data->keys + *keyp;
1314 /* Skip the data associated with this old key. */
1315 if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
1317 ndp = data_skip(dp, REPOKEY_TYPE_ID);
1318 ndp = data_skip(ndp, REPOKEY_TYPE_ID);
1320 else if (key->storage == KEY_STORAGE_INCORE)
1321 ndp = data_skip(dp, key->type);
1324 if (seen[*keyp] == -1)
1326 /* If this key was an old one _and_ was not overwritten with
1327 a different value copy over the old value (we skipped it
1330 data_addblob(&newincore, dp, ndp - dp);
1333 else if (seen[*keyp])
1335 /* Otherwise we have a new value. Parse it into the internal
1338 unsigned int oldvincorelen = 0;
1341 if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
1344 oldvincorelen = xd->len;
1346 id = seen[*keyp] - 1;
1349 case REPOKEY_TYPE_VOID:
1350 case REPOKEY_TYPE_CONSTANT:
1351 case REPOKEY_TYPE_CONSTANTID:
1353 case REPOKEY_TYPE_STR:
1354 data_addblob(xd, data->attrdata + id, strlen((char *)(data->attrdata + id)) + 1);
1356 case REPOKEY_TYPE_ID:
1357 case REPOKEY_TYPE_NUM:
1358 case REPOKEY_TYPE_DIR:
1361 case REPOKEY_TYPE_DIRNUMNUMARRAY:
1362 for (ida = data->attriddata + id; *ida; ida += 3)
1364 data_addid(xd, ida[0]);
1365 data_addid(xd, ida[1]);
1366 data_addideof(xd, ida[2], ida[3] ? 0 : 1);
1369 case REPOKEY_TYPE_DIRSTRARRAY:
1370 for (ida = data->attriddata + id; *ida; ida += 2)
1372 data_addideof(xd, ida[0], ida[2] ? 0 : 1);
1373 data_addblob(xd, data->attrdata + ida[1], strlen((char *)(data->attrdata + ida[1])) + 1);
1377 fprintf(stderr, "don't know how to handle type %d\n", key->type);
1380 if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
1382 /* put offset/len in incore */
1383 data_addid(&newincore, data->lastverticaloffset + oldvincorelen);
1384 oldvincorelen = xd->len - oldvincorelen;
1385 data_addid(&newincore, oldvincorelen);
1390 if (data->attrs[entry])
1391 sat_free(data->attrs[entry]);
1396 sat_free(data->incoredata);
1397 data->incoredata = newincore.buf;
1398 data->incoredatalen = newincore.len;
1399 data->incoredatafree = 0;
1401 sat_free(data->vincore);
1402 data->vincore = newvincore.buf;
1403 data->vincorelen = newvincore.len;
1405 data->attrs = sat_free(data->attrs);
1406 data->attrdata = sat_free(data->attrdata);
1407 data->attriddata = sat_free(data->attriddata);
1408 data->attrdatalen = 0;
1409 data->attriddatalen = 0;
1413 repodata_str2dir(Repodata *data, const char *dir, int create)
1419 while (*dir == '/' && dir[1] == '/')
1423 dire = strchrnul(dir, '/');
1424 if (data->localpool)
1425 id = stringpool_strn2id(&data->spool, dir, dire - dir, create);
1427 id = strn2id(data->repo->pool, dir, dire - dir, create);
1430 parent = dirpool_add_dir(&data->dirpool, parent, id, create);
1443 repodata_compress_page(unsigned char *page, unsigned int len, unsigned char *cpage, unsigned int max)
1445 return compress_buf(page, len, cpage, max);
1448 #define SOLV_ERROR_EOF 3
1450 static inline unsigned int
1456 for (i = 0; i < 4; i++)
1466 #define SOLV_ERROR_EOF 3
1467 #define SOLV_ERROR_CORRUPT 6
1469 /* Try to either setup on-demand paging (using FP as backing
1470 file), or in case that doesn't work (FP not seekable) slurps in
1471 all pages and deactivates paging. */
1473 repodata_read_or_setup_pages(Repodata *data, unsigned int pagesz, unsigned int blobsz)
1475 FILE *fp = data->fp;
1476 unsigned int npages;
1478 unsigned int can_seek;
1480 unsigned char buf[BLOB_PAGESIZE];
1482 if (pagesz != BLOB_PAGESIZE)
1484 /* We could handle this by slurping in everything. */
1485 data->error = SOLV_ERROR_CORRUPT;
1489 if ((cur_file_ofs = ftell(fp)) < 0)
1493 data->pagefd = dup(fileno(fp));
1494 if (data->pagefd == -1)
1498 fprintf (stderr, "can %sseek\n", can_seek ? "" : "NOT ");
1500 npages = (blobsz + BLOB_PAGESIZE - 1) / BLOB_PAGESIZE;
1502 data->num_pages = npages;
1503 data->pages = sat_malloc2(npages, sizeof(data->pages[0]));
1505 /* If we can't seek on our input we have to slurp in everything. */
1507 data->blob_store = sat_malloc(npages * BLOB_PAGESIZE);
1508 for (i = 0; i < npages; i++)
1510 unsigned int in_len = read_u32(fp);
1511 unsigned int compressed = in_len & 1;
1512 Attrblobpage *p = data->pages + i;
1515 fprintf (stderr, "page %d: len %d (%scompressed)\n",
1516 i, in_len, compressed ? "" : "not ");
1522 p->file_offset = cur_file_ofs;
1523 p->file_size = in_len * 2 + compressed;
1524 if (fseek(fp, in_len, SEEK_CUR) < 0)
1527 fprintf (stderr, "can't seek after we thought we can\n");
1528 /* We can't fall back to non-seeking behaviour as we already
1529 read over some data pages without storing them away. */
1530 data->error = SOLV_ERROR_EOF;
1531 close(data->pagefd);
1535 cur_file_ofs += in_len;
1539 unsigned int out_len;
1540 void *dest = data->blob_store + i * BLOB_PAGESIZE;
1541 p->mapped_at = i * BLOB_PAGESIZE;
1544 /* We can't seek, so suck everything in. */
1545 if (fread(compressed ? buf : dest, in_len, 1, fp) != 1)
1548 data->error = SOLV_ERROR_EOF;
1553 out_len = unchecked_decompress_buf(buf, in_len, dest, BLOB_PAGESIZE);
1554 if (out_len != BLOB_PAGESIZE && i < npages - 1)
1556 data->error = SOLV_ERROR_CORRUPT;
1565 vim:cinoptions={.5s,g0,p5,t0,(0,^-0.5s,n-0.5s:tw=78:cindent:sw=4: