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, Id *keyid)
306 if (data->state == REPODATA_STUB)
308 if (data->loadcallback)
312 /* key order may change when loading */
314 Id name = data->keys[*keyid].name;
315 Id type = data->keys[*keyid].type;
316 data->loadcallback(data);
317 if (data->state == REPODATA_AVAILABLE)
319 for (i = 1; i < data->nkeys; i++)
320 if (data->keys[i].name == name && data->keys[i].type == type)
329 data->loadcallback(data);
332 data->state = REPODATA_ERROR;
334 if (data->state == REPODATA_AVAILABLE)
336 data->state = REPODATA_ERROR;
341 repodata_lookup_str(Repodata *data, Id entry, Id keyid)
348 if (!maybe_load_repodata(data, &keyid))
351 dp = data->incoredata + data->incoreoffset[entry];
352 dp = data_read_id(dp, &schema);
353 /* make sure the schema of this solvable contains the key */
354 for (keyp = data->schemadata + data->schemata[schema]; *keyp != keyid; keyp++)
357 dp = forward_to_key(data, keyid, schema, dp);
358 key = data->keys + keyid;
359 dp = get_data(data, key, &dp);
362 if (key->type == REPOKEY_TYPE_STR)
363 return (const char *)dp;
364 if (key->type == REPOKEY_TYPE_CONSTANTID)
365 return id2str(data->repo->pool, key->size);
366 if (key->type == REPOKEY_TYPE_ID)
367 dp = data_read_id(dp, &id);
371 return data->spool.stringspace + data->spool.strings[id];
372 return id2str(data->repo->pool, id);
376 repodata_lookup_num(Repodata *data, Id entry, Id keyid, unsigned *value)
386 if (!maybe_load_repodata(data, &keyid))
389 dp = data->incoredata + data->incoreoffset[entry];
390 dp = data_read_id(dp, &schema);
391 /* make sure the schema of this solvable contains the key */
392 for (keyp = data->schemadata + data->schemata[schema]; *keyp != keyid; keyp++)
395 dp = forward_to_key(data, keyid, schema, dp);
396 key = data->keys + keyid;
397 dp = get_data(data, key, &dp);
400 if (key->type == REPOKEY_TYPE_NUM
401 || key->type == REPOKEY_TYPE_U32
402 || key->type == REPOKEY_TYPE_CONSTANT)
404 dp = data_fetch(dp, &kv, key);
412 repodata_search(Repodata *data, Id entry, Id keyname, int (*callback)(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv), void *cbdata)
416 Id k, keyid, *kp, *keyp;
417 unsigned char *dp, *ddp;
422 if (!maybe_load_repodata(data, 0))
425 dp = data->incoredata + data->incoreoffset[entry];
426 dp = data_read_id(dp, &schema);
427 keyp = data->schemadata + data->schemata[schema];
430 /* search in a specific key */
431 for (kp = keyp; (k = *kp++) != 0; )
432 if (data->keys[k].name == keyname)
436 dp = forward_to_key(data, k, schema, dp);
442 while ((keyid = *keyp++) != 0)
445 key = data->keys + keyid;
446 ddp = get_data(data, key, &dp);
449 ddp = data_fetch(ddp, &kv, key);
452 stop = callback(cbdata, data->repo->pool->solvables + data->start + entry, data, key, &kv);
454 while (!kv.eof && !stop);
455 if (onekey || stop > SEARCH_NEXT_KEY)
461 dataiterator_newdata(Dataiterator *di)
463 Id keyname = di->keyname;
464 Repodata *data = di->data;
467 if (data->state == REPODATA_STUB)
472 for (j = 1; j < data->nkeys; j++)
473 if (keyname == data->keys[j].name)
475 if (j == data->nkeys)
479 if (data->loadcallback)
480 data->loadcallback(data);
482 data->state = REPODATA_ERROR;
484 if (data->state == REPODATA_ERROR)
488 unsigned char *dp = data->incoredata + data->incoreoffset[di->solvid - data->start];
489 dp = data_read_id(dp, &schema);
490 Id *keyp = data->schemadata + data->schemata[schema];
494 /* search in a specific key */
495 for (kp = keyp; (k = *kp++) != 0; )
496 if (data->keys[k].name == keyname)
500 dp = forward_to_key(data, k, schema, dp);
510 di->key = di->data->keys + keyid;
515 di->dp = get_data(di->data, di->key, &di->nextkeydp);
520 dataiterator_init(Dataiterator *di, Repo *repo, Id p, Id keyname,
521 const char *match, int flags)
527 di->flags |= __SEARCH_ONESOLVABLE;
528 di->data = repo->repodata - 1;
529 if (flags & SEARCH_NO_STORAGE_SOLVABLE)
536 di->solvid = repo->start - 1;
537 di->data = repo->repodata + repo->nrepodata - 1;
541 di->keyname = keyname;
542 static Id zeroid = 0;
549 /* FIXME factor and merge with repo_matchvalue */
551 dataiterator_match(Dataiterator *di, KeyValue *kv)
553 int flags = di->flags;
555 if ((flags & SEARCH_STRINGMASK) != 0)
557 switch (di->key->type)
559 case REPOKEY_TYPE_ID:
560 case REPOKEY_TYPE_IDARRAY:
561 if (di->data && di->data->localpool)
562 kv->str = stringpool_id2str(&di->data->spool, kv->id);
564 kv->str = id2str(di->repo->pool, kv->id);
566 case REPOKEY_TYPE_STR:
571 switch ((flags & SEARCH_STRINGMASK))
573 case SEARCH_SUBSTRING:
574 if (flags & SEARCH_NOCASE)
576 if (!strcasestr(kv->str, di->match))
581 if (!strstr(kv->str, di->match))
586 if (flags & SEARCH_NOCASE)
588 if (strcasecmp(di->match, kv->str))
593 if (strcmp(di->match, kv->str))
598 if (fnmatch(di->match, kv->str, (flags & SEARCH_NOCASE) ? FNM_CASEFOLD : 0))
603 if (regexec(&di->regexp, kv->str, 0, NULL, 0))
613 static Repokey solvablekeys[RPM_RPMDBID - SOLVABLE_NAME + 1] = {
614 { SOLVABLE_NAME, REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
615 { SOLVABLE_ARCH, REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
616 { SOLVABLE_EVR, REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
617 { SOLVABLE_VENDOR, REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
618 { SOLVABLE_PROVIDES, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
619 { SOLVABLE_OBSOLETES, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
620 { SOLVABLE_CONFLICTS, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
621 { SOLVABLE_REQUIRES, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
622 { SOLVABLE_RECOMMENDS, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
623 { SOLVABLE_SUGGESTS, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
624 { SOLVABLE_SUPPLEMENTS, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
625 { SOLVABLE_ENHANCES, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
626 { SOLVABLE_FRESHENS, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
627 { RPM_RPMDBID, REPOKEY_TYPE_U32, 0, KEY_STORAGE_SOLVABLE },
631 dataiterator_step(Dataiterator *di)
645 di->kv.eof = idp[1] ? 0 : 1;
651 Solvable *s = di->repo->pool->solvables + di->solvid;
652 int state = di->state;
653 di->key = solvablekeys + state - 1;
655 di->state = RPM_RPMDBID;
662 state = di->keyname - 1;
681 case SOLVABLE_VENDOR:
684 di->kv.id = s->vendor;
686 case SOLVABLE_PROVIDES:
687 di->idp = s->provides
688 ? di->repo->idarraydata + s->provides : 0;
690 case SOLVABLE_OBSOLETES:
691 di->idp = s->obsoletes
692 ? di->repo->idarraydata + s->obsoletes : 0;
694 case SOLVABLE_CONFLICTS:
695 di->idp = s->conflicts
696 ? di->repo->idarraydata + s->conflicts : 0;
698 case SOLVABLE_REQUIRES:
699 di->idp = s->requires
700 ? di->repo->idarraydata + s->requires : 0;
702 case SOLVABLE_RECOMMENDS:
703 di->idp = s->recommends
704 ? di->repo->idarraydata + s->recommends : 0;
706 case SOLVABLE_SUPPLEMENTS:
707 di->idp = s->supplements
708 ? di->repo->idarraydata + s->supplements : 0;
710 case SOLVABLE_SUGGESTS:
711 di->idp = s->suggests
712 ? di->repo->idarraydata + s->suggests : 0;
714 case SOLVABLE_ENHANCES:
715 di->idp = s->enhances
716 ? di->repo->idarraydata + s->enhances : 0;
718 case SOLVABLE_FRESHENS:
719 di->idp = s->freshens
720 ? di->repo->idarraydata + s->freshens : 0;
723 if (!di->repo->rpmdbid)
725 di->kv.num = di->repo->rpmdbid[di->solvid - di->repo->start];
728 di->data = di->repo->repodata - 1;
739 di->dp = data_fetch(di->dp, &di->kv, di->key);
744 if (di->keyname || !(keyid = *di->keyp++))
748 Repo *repo = di->repo;
749 Repodata *data = ++di->data;
750 if (data >= repo->repodata + repo->nrepodata)
752 if (di->flags & __SEARCH_ONESOLVABLE)
754 while (++di->solvid < repo->end)
755 if (repo->pool->solvables[di->solvid].repo == repo)
757 if (di->solvid >= repo->end)
759 di->data = repo->repodata - 1;
760 if (di->flags & SEARCH_NO_STORAGE_SOLVABLE)
762 static Id zeroid = 0;
767 if (di->solvid >= data->start && di->solvid < data->end)
769 dataiterator_newdata(di);
777 di->key = di->data->keys + keyid;
778 di->dp = get_data(di->data, di->key, &di->nextkeydp);
780 di->dp = data_fetch(di->dp, &di->kv, di->key);
785 || dataiterator_match(di, &di->kv))
792 repodata_init(Repodata *data, Repo *repo, int localpool)
794 memset(data, 0, sizeof (*data));
796 data->localpool = localpool;
798 stringpool_init_empty(&data->spool);
799 data->keys = sat_calloc(1, sizeof(Repokey));
801 data->schemata = sat_calloc(1, sizeof(Id));
802 data->schemadata = sat_calloc(1, sizeof(Id));
804 data->schemadatalen = 1;
805 data->start = repo->start;
806 data->end = repo->end;
807 data->incoreoffset = sat_extend_resize(0, data->end - data->start, sizeof(Id), REPODATA_BLOCK);
811 /* extend repodata so that it includes solvables p */
813 repodata_extend(Repodata *data, Id p)
815 if (data->start == data->end)
816 data->start = data->end = p;
819 int old = data->end - data->start;
820 int new = p - data->end + 1;
823 data->attrs = sat_extend(data->attrs, old, new, sizeof(Id *), REPODATA_BLOCK);
824 memset(data->attrs + old, 0, new * sizeof(Id *));
826 data->incoreoffset = sat_extend(data->incoreoffset, old, new, sizeof(Id), REPODATA_BLOCK);
827 memset(data->incoreoffset + old, 0, new * sizeof(Id));
832 int old = data->end - data->start;
833 int new = data->start - p;
836 data->attrs = sat_extend_resize(data->attrs, old + new, sizeof(Id *), REPODATA_BLOCK);
837 memmove(data->attrs + new, data->attrs, old * sizeof(Id *));
838 memset(data->attrs, 0, new * sizeof(Id *));
840 data->incoreoffset = sat_extend_resize(data->incoreoffset, old + new, sizeof(Id), REPODATA_BLOCK);
841 memmove(data->incoreoffset + new, data->incoreoffset, old * sizeof(Id));
842 memset(data->incoreoffset, 0, new * sizeof(Id));
848 repodata_extend_block(Repodata *data, Id start, Id num)
852 if (!data->incoreoffset)
854 data->incoreoffset = sat_extend_resize(data->incoreoffset, num, sizeof(Id), REPODATA_BLOCK);
855 memset(data->incoreoffset, 0, num * sizeof(Id));
857 data->end = start + num;
860 repodata_extend(data, start);
862 repodata_extend(data, start + num - 1);
865 #define REPODATA_ATTRS_BLOCK 63
866 #define REPODATA_ATTRDATA_BLOCK 1023
867 #define REPODATA_ATTRIDDATA_BLOCK 63
870 repodata_insert_keyid(Repodata *data, Id entry, Id keyid, Id val, int overwrite)
876 data->attrs = sat_extend_resize(0, data->end - data->start, sizeof(Id *), REPODATA_BLOCK);
877 memset(data->attrs, 0, (data->end - data->start) * sizeof(Id *));
880 if (data->attrs[entry])
882 for (pp = data->attrs[entry]; *pp; pp += 2)
883 /* Determine equality based on the name only, allows us to change
884 type (when overwrite is set), and makes TYPE_CONSTANT work. */
885 if (data->keys[*pp].name == data->keys[keyid].name)
896 i = pp - data->attrs[entry];
898 data->attrs[entry] = sat_extend(data->attrs[entry], i, 3, sizeof(Id), REPODATA_ATTRS_BLOCK);
899 pp = data->attrs[entry] + i;
906 repodata_set(Repodata *data, Id entry, Repokey *key, Id val)
910 /* find key in keys */
911 for (keyid = 1; keyid < data->nkeys; keyid++)
912 if (data->keys[keyid].name == key->name && data->keys[keyid].type == key->type)
914 if ((key->type == REPOKEY_TYPE_CONSTANT || key->type == REPOKEY_TYPE_CONSTANTID) && key->size != data->keys[keyid].size)
918 if (keyid == data->nkeys)
920 /* allocate new key */
921 data->keys = sat_realloc2(data->keys, data->nkeys + 1, sizeof(Repokey));
922 data->keys[data->nkeys++] = *key;
923 if (data->verticaloffset)
925 data->verticaloffset = sat_realloc2(data->verticaloffset, data->nkeys, sizeof(Id));
926 data->verticaloffset[data->nkeys - 1] = 0;
929 repodata_insert_keyid(data, entry, keyid, val, 1);
933 repodata_set_id(Repodata *data, Id entry, Id keyname, Id id)
937 key.type = REPOKEY_TYPE_ID;
939 key.storage = KEY_STORAGE_INCORE;
940 repodata_set(data, entry, &key, id);
944 repodata_set_num(Repodata *data, Id entry, Id keyname, Id num)
948 key.type = REPOKEY_TYPE_NUM;
950 key.storage = KEY_STORAGE_INCORE;
951 repodata_set(data, entry, &key, num);
955 repodata_set_poolstr(Repodata *data, Id entry, Id keyname, const char *str)
960 id = stringpool_str2id(&data->spool, str, 1);
962 id = str2id(data->repo->pool, str, 1);
964 key.type = REPOKEY_TYPE_ID;
966 key.storage = KEY_STORAGE_INCORE;
967 repodata_set(data, entry, &key, id);
971 repodata_set_constant(Repodata *data, Id entry, Id keyname, Id constant)
975 key.type = REPOKEY_TYPE_CONSTANT;
977 key.storage = KEY_STORAGE_INCORE;
978 repodata_set(data, entry, &key, 0);
982 repodata_set_constantid(Repodata *data, Id entry, Id keyname, Id id)
986 key.type = REPOKEY_TYPE_CONSTANTID;
988 key.storage = KEY_STORAGE_INCORE;
989 repodata_set(data, entry, &key, 0);
993 repodata_set_void(Repodata *data, Id entry, Id keyname)
997 key.type = REPOKEY_TYPE_VOID;
999 key.storage = KEY_STORAGE_INCORE;
1000 repodata_set(data, entry, &key, 0);
1004 repodata_set_str(Repodata *data, Id entry, Id keyname, const char *str)
1009 l = strlen(str) + 1;
1011 key.type = REPOKEY_TYPE_STR;
1013 key.storage = KEY_STORAGE_INCORE;
1014 data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
1015 memcpy(data->attrdata + data->attrdatalen, str, l);
1016 repodata_set(data, entry, &key, data->attrdatalen);
1017 data->attrdatalen += l;
1021 repodata_add_dirnumnum(Repodata *data, Id entry, Id keyname, Id dir, Id num, Id num2)
1027 fprintf(stderr, "repodata_add_dirnumnum %d %d %d %d (%d)\n", entry, dir, num, num2, data->attriddatalen);
1029 if (data->attrs && data->attrs[entry])
1031 for (pp = data->attrs[entry]; *pp; pp += 2)
1032 if (data->keys[*pp].name == keyname && data->keys[*pp].type == REPOKEY_TYPE_DIRNUMNUMARRAY)
1037 for (ida = data->attriddata + pp[1]; *ida; ida += 3)
1039 if (ida + 1 == data->attriddata + data->attriddatalen)
1041 /* this was the last entry, just append it */
1042 data->attriddata = sat_extend(data->attriddata, data->attriddatalen, 3, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1043 data->attriddatalen--; /* overwrite terminating 0 */
1047 /* too bad. move to back. */
1048 data->attriddata = sat_extend(data->attriddata, data->attriddatalen, oldsize + 4, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1049 memcpy(data->attriddata + data->attriddatalen, data->attriddata + pp[1], oldsize * sizeof(Id));
1050 pp[1] = data->attriddatalen;
1051 data->attriddatalen += oldsize;
1053 data->attriddata[data->attriddatalen++] = dir;
1054 data->attriddata[data->attriddatalen++] = num;
1055 data->attriddata[data->attriddatalen++] = num2;
1056 data->attriddata[data->attriddatalen++] = 0;
1061 key.type = REPOKEY_TYPE_DIRNUMNUMARRAY;
1063 key.storage = KEY_STORAGE_INCORE;
1064 data->attriddata = sat_extend(data->attriddata, data->attriddatalen, 4, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1065 repodata_set(data, entry, &key, data->attriddatalen);
1066 data->attriddata[data->attriddatalen++] = dir;
1067 data->attriddata[data->attriddatalen++] = num;
1068 data->attriddata[data->attriddatalen++] = num2;
1069 data->attriddata[data->attriddatalen++] = 0;
1073 repodata_add_dirstr(Repodata *data, Id entry, Id keyname, Id dir, const char *str)
1075 Id *ida, *pp, stroff;
1079 l = strlen(str) + 1;
1080 data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
1081 memcpy(data->attrdata + data->attrdatalen, str, l);
1082 stroff = data->attrdatalen;
1083 data->attrdatalen += l;
1086 fprintf(stderr, "repodata_add_dirstr %d %d %s (%d)\n", entry, dir, str, data->attriddatalen);
1088 if (data->attrs && data->attrs[entry])
1090 for (pp = data->attrs[entry]; *pp; pp += 2)
1091 if (data->keys[*pp].name == keyname && data->keys[*pp].type == REPOKEY_TYPE_DIRSTRARRAY)
1096 for (ida = data->attriddata + pp[1]; *ida; ida += 2)
1098 if (ida + 1 == data->attriddata + data->attriddatalen)
1100 /* this was the last entry, just append it */
1101 data->attriddata = sat_extend(data->attriddata, data->attriddatalen, 2, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1102 data->attriddatalen--; /* overwrite terminating 0 */
1106 /* too bad. move to back. */
1107 data->attriddata = sat_extend(data->attriddata, data->attriddatalen, oldsize + 3, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1108 memcpy(data->attriddata + data->attriddatalen, data->attriddata + pp[1], oldsize * sizeof(Id));
1109 pp[1] = data->attriddatalen;
1110 data->attriddatalen += oldsize;
1112 data->attriddata[data->attriddatalen++] = dir;
1113 data->attriddata[data->attriddatalen++] = stroff;
1114 data->attriddata[data->attriddatalen++] = 0;
1119 key.type = REPOKEY_TYPE_DIRSTRARRAY;
1121 key.storage = KEY_STORAGE_INCORE;
1122 data->attriddata = sat_extend(data->attriddata, data->attriddatalen, 3, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1123 repodata_set(data, entry, &key, data->attriddatalen);
1124 data->attriddata[data->attriddatalen++] = dir;
1125 data->attriddata[data->attriddatalen++] = stroff;
1126 data->attriddata[data->attriddatalen++] = 0;
1130 repodata_merge_attrs (Repodata *data, Id dest, Id src)
1133 for (keyp = data->attrs[src]; *keyp; keyp += 2)
1134 repodata_insert_keyid(data, dest, keyp[0], keyp[1], 0);
1137 /*********************************/
1139 /* unify with repo_write! */
1141 #define EXTDATA_BLOCK 1023
1142 #define SCHEMATA_BLOCK 31
1143 #define SCHEMATADATA_BLOCK 255
1151 data_addid(struct extdata *xd, Id x)
1154 xd->buf = sat_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
1155 dp = xd->buf + xd->len;
1160 *dp++ = (x >> 28) | 128;
1162 *dp++ = (x >> 21) | 128;
1163 *dp++ = (x >> 14) | 128;
1166 *dp++ = (x >> 7) | 128;
1168 xd->len = dp - xd->buf;
1172 data_addideof(struct extdata *xd, Id x, int eof)
1175 x = (x & 63) | ((x & ~63) << 1);
1176 data_addid(xd, (eof ? x: x | 64));
1180 data_addblob(struct extdata *xd, unsigned char *blob, int len)
1182 xd->buf = sat_extend(xd->buf, xd->len, len, 1, EXTDATA_BLOCK);
1183 memcpy(xd->buf + xd->len, blob, len);
1187 /*********************************/
1190 addschema_prepare(Repodata *data, Id *schematacache)
1195 memset(schematacache, 0, 256 * sizeof(Id));
1196 for (i = 0; i < data->nschemata; i++)
1198 for (sp = data->schemadata + data->schemata[i], h = 0; *sp; len++)
1201 schematacache[h] = i + 1;
1203 data->schemadata = sat_extend_resize(data->schemadata, data->schemadatalen, sizeof(Id), SCHEMATADATA_BLOCK);
1204 data->schemata = sat_extend_resize(data->schemata, data->nschemata, sizeof(Id), SCHEMATA_BLOCK);
1208 addschema(Repodata *data, Id *schema, Id *schematacache)
1213 for (sp = schema, len = 0, h = 0; *sp; len++)
1218 cid = schematacache[h];
1222 if (!memcmp(data->schemadata + data->schemata[cid], schema, len * sizeof(Id)))
1224 /* cache conflict */
1225 for (cid = 0; cid < data->nschemata; cid++)
1226 if (!memcmp(data->schemadata + data->schemata[cid], schema, len * sizeof(Id)))
1229 /* a new one. make room. */
1230 data->schemadata = sat_extend(data->schemadata, data->schemadatalen, len, sizeof(Id), SCHEMATADATA_BLOCK);
1231 data->schemata = sat_extend(data->schemata, data->nschemata, 1, sizeof(Id), SCHEMATA_BLOCK);
1233 memcpy(data->schemadata + data->schemadatalen, schema, len * sizeof(Id));
1234 data->schemata[data->nschemata] = data->schemadatalen;
1235 data->schemadatalen += len;
1236 schematacache[h] = data->nschemata + 1;
1238 fprintf(stderr, "addschema: new schema\n");
1240 return data->nschemata++;
1245 repodata_internalize(Repodata *data)
1248 Id id, entry, nentry, *ida;
1249 Id schematacache[256];
1250 Id schemaid, *schema, *sp, oldschema, *keyp, *seen;
1251 unsigned char *dp, *ndp;
1252 int newschema, oldcount;
1253 struct extdata newincore;
1254 struct extdata newvincore;
1259 newvincore.buf = data->vincore;
1260 newvincore.len = data->vincorelen;
1262 schema = sat_malloc2(data->nkeys, sizeof(Id));
1263 seen = sat_malloc2(data->nkeys, sizeof(Id));
1265 /* Merge the data already existing (in data->schemata, ->incoredata and
1266 friends) with the new attributes in data->attrs[]. */
1267 nentry = data->end - data->start;
1268 addschema_prepare(data, schematacache);
1269 memset(&newincore, 0, sizeof(newincore));
1270 for (entry = 0; entry < nentry; entry++)
1272 memset(seen, 0, data->nkeys * sizeof(Id));
1274 dp = data->incoredata + data->incoreoffset[entry];
1275 if (data->incoredata)
1276 dp = data_read_id(dp, &oldschema);
1280 fprintf(stderr, "oldschema %d\n", oldschema);
1281 fprintf(stderr, "schemata %d\n", data->schemata[oldschema]);
1282 fprintf(stderr, "schemadata %p\n", data->schemadata);
1284 /* seen: -1: old data 0: skipped >0: id + 1 */
1287 for (keyp = data->schemadata + data->schemata[oldschema]; *keyp; keyp++)
1291 fprintf(stderr, "Inconsistent old data (key occured twice).\n");
1298 if (data->attrs[entry])
1299 for (keyp = data->attrs[entry]; *keyp; keyp += 2)
1306 seen[*keyp] = keyp[1] + 1;
1310 /* Ideally we'd like to sort the new schema here, to ensure
1311 schema equality independend of the ordering. We can't do that
1312 yet. For once see below (old ids need to come before new ids).
1313 An additional difficulty is that we also need to move
1314 the values with the keys. */
1315 schemaid = addschema(data, schema, schematacache);
1317 schemaid = oldschema;
1320 /* Now create data blob. We walk through the (possibly new) schema
1321 and either copy over old data, or insert the new. */
1322 /* XXX Here we rely on the fact that the (new) schema has the form
1323 o1 o2 o3 o4 ... | n1 n2 n3 ...
1324 (oX being the old keyids (possibly overwritten), and nX being
1325 the new keyids). This rules out sorting the keyids in order
1326 to ensure a small schema count. */
1327 data->incoreoffset[entry] = newincore.len;
1328 data_addid(&newincore, schemaid);
1329 for (keyp = data->schemadata + data->schemata[schemaid]; *keyp; keyp++)
1331 key = data->keys + *keyp;
1335 /* Skip the data associated with this old key. */
1336 if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
1338 ndp = data_skip(dp, REPOKEY_TYPE_ID);
1339 ndp = data_skip(ndp, REPOKEY_TYPE_ID);
1341 else if (key->storage == KEY_STORAGE_INCORE)
1342 ndp = data_skip(dp, key->type);
1345 if (seen[*keyp] == -1)
1347 /* If this key was an old one _and_ was not overwritten with
1348 a different value copy over the old value (we skipped it
1351 data_addblob(&newincore, dp, ndp - dp);
1354 else if (seen[*keyp])
1356 /* Otherwise we have a new value. Parse it into the internal
1359 unsigned int oldvincorelen = 0;
1362 if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
1365 oldvincorelen = xd->len;
1367 id = seen[*keyp] - 1;
1370 case REPOKEY_TYPE_VOID:
1371 case REPOKEY_TYPE_CONSTANT:
1372 case REPOKEY_TYPE_CONSTANTID:
1374 case REPOKEY_TYPE_STR:
1375 data_addblob(xd, data->attrdata + id, strlen((char *)(data->attrdata + id)) + 1);
1377 case REPOKEY_TYPE_ID:
1378 case REPOKEY_TYPE_NUM:
1379 case REPOKEY_TYPE_DIR:
1382 case REPOKEY_TYPE_DIRNUMNUMARRAY:
1383 for (ida = data->attriddata + id; *ida; ida += 3)
1385 data_addid(xd, ida[0]);
1386 data_addid(xd, ida[1]);
1387 data_addideof(xd, ida[2], ida[3] ? 0 : 1);
1390 case REPOKEY_TYPE_DIRSTRARRAY:
1391 for (ida = data->attriddata + id; *ida; ida += 2)
1393 data_addideof(xd, ida[0], ida[2] ? 0 : 1);
1394 data_addblob(xd, data->attrdata + ida[1], strlen((char *)(data->attrdata + ida[1])) + 1);
1398 fprintf(stderr, "don't know how to handle type %d\n", key->type);
1401 if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
1403 /* put offset/len in incore */
1404 data_addid(&newincore, data->lastverticaloffset + oldvincorelen);
1405 oldvincorelen = xd->len - oldvincorelen;
1406 data_addid(&newincore, oldvincorelen);
1411 if (data->attrs[entry])
1412 sat_free(data->attrs[entry]);
1417 sat_free(data->incoredata);
1418 data->incoredata = newincore.buf;
1419 data->incoredatalen = newincore.len;
1420 data->incoredatafree = 0;
1422 sat_free(data->vincore);
1423 data->vincore = newvincore.buf;
1424 data->vincorelen = newvincore.len;
1426 data->attrs = sat_free(data->attrs);
1427 data->attrdata = sat_free(data->attrdata);
1428 data->attriddata = sat_free(data->attriddata);
1429 data->attrdatalen = 0;
1430 data->attriddatalen = 0;
1434 repodata_str2dir(Repodata *data, const char *dir, int create)
1440 while (*dir == '/' && dir[1] == '/')
1444 dire = strchrnul(dir, '/');
1445 if (data->localpool)
1446 id = stringpool_strn2id(&data->spool, dir, dire - dir, create);
1448 id = strn2id(data->repo->pool, dir, dire - dir, create);
1451 parent = dirpool_add_dir(&data->dirpool, parent, id, create);
1464 repodata_compress_page(unsigned char *page, unsigned int len, unsigned char *cpage, unsigned int max)
1466 return compress_buf(page, len, cpage, max);
1469 #define SOLV_ERROR_EOF 3
1471 static inline unsigned int
1477 for (i = 0; i < 4; i++)
1487 #define SOLV_ERROR_EOF 3
1488 #define SOLV_ERROR_CORRUPT 6
1490 /* Try to either setup on-demand paging (using FP as backing
1491 file), or in case that doesn't work (FP not seekable) slurps in
1492 all pages and deactivates paging. */
1494 repodata_read_or_setup_pages(Repodata *data, unsigned int pagesz, unsigned int blobsz)
1496 FILE *fp = data->fp;
1497 unsigned int npages;
1499 unsigned int can_seek;
1501 unsigned char buf[BLOB_PAGESIZE];
1503 if (pagesz != BLOB_PAGESIZE)
1505 /* We could handle this by slurping in everything. */
1506 data->error = SOLV_ERROR_CORRUPT;
1510 if ((cur_file_ofs = ftell(fp)) < 0)
1514 data->pagefd = dup(fileno(fp));
1515 if (data->pagefd == -1)
1519 fprintf (stderr, "can %sseek\n", can_seek ? "" : "NOT ");
1521 npages = (blobsz + BLOB_PAGESIZE - 1) / BLOB_PAGESIZE;
1523 data->num_pages = npages;
1524 data->pages = sat_malloc2(npages, sizeof(data->pages[0]));
1526 /* If we can't seek on our input we have to slurp in everything. */
1528 data->blob_store = sat_malloc(npages * BLOB_PAGESIZE);
1529 for (i = 0; i < npages; i++)
1531 unsigned int in_len = read_u32(fp);
1532 unsigned int compressed = in_len & 1;
1533 Attrblobpage *p = data->pages + i;
1536 fprintf (stderr, "page %d: len %d (%scompressed)\n",
1537 i, in_len, compressed ? "" : "not ");
1543 p->file_offset = cur_file_ofs;
1544 p->file_size = in_len * 2 + compressed;
1545 if (fseek(fp, in_len, SEEK_CUR) < 0)
1548 fprintf (stderr, "can't seek after we thought we can\n");
1549 /* We can't fall back to non-seeking behaviour as we already
1550 read over some data pages without storing them away. */
1551 data->error = SOLV_ERROR_EOF;
1552 close(data->pagefd);
1556 cur_file_ofs += in_len;
1560 unsigned int out_len;
1561 void *dest = data->blob_store + i * BLOB_PAGESIZE;
1562 p->mapped_at = i * BLOB_PAGESIZE;
1565 /* We can't seek, so suck everything in. */
1566 if (fread(compressed ? buf : dest, in_len, 1, fp) != 1)
1569 data->error = SOLV_ERROR_EOF;
1574 out_len = unchecked_decompress_buf(buf, in_len, dest, BLOB_PAGESIZE);
1575 if (out_len != BLOB_PAGESIZE && i < npages - 1)
1577 data->error = SOLV_ERROR_CORRUPT;
1586 vim:cinoptions={.5s,g0,p5,t0,(0,^-0.5s,n-0.5s:tw=78:cindent:sw=4: