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++)
453 const unsigned char *
454 repodata_lookup_bin_checksum(Repodata *data, Id entry, Id keyid, Id *typep)
461 if (!maybe_load_repodata(data, &keyid))
463 dp = data->incoredata + data->incoreoffset[entry];
464 dp = data_read_id(dp, &schema);
465 for (keyp = data->schemadata + data->schemata[schema]; *keyp != keyid; keyp++)
468 dp = forward_to_key(data, keyid, schema, dp);
469 key = data->keys + keyid;
471 return get_data(data, key, &dp);
475 repodata_search(Repodata *data, Id entry, Id keyname, int (*callback)(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv), void *cbdata)
479 Id k, keyid, *kp, *keyp;
480 unsigned char *dp, *ddp;
485 if (!maybe_load_repodata(data, 0))
488 dp = data->incoredata + data->incoreoffset[entry];
489 dp = data_read_id(dp, &schema);
490 keyp = data->schemadata + data->schemata[schema];
493 /* search in a specific key */
494 for (kp = keyp; (k = *kp++) != 0; )
495 if (data->keys[k].name == keyname)
499 dp = forward_to_key(data, k, schema, dp);
505 while ((keyid = *keyp++) != 0)
508 key = data->keys + keyid;
509 ddp = get_data(data, key, &dp);
512 ddp = data_fetch(ddp, &kv, key);
515 stop = callback(cbdata, data->repo->pool->solvables + data->start + entry, data, key, &kv);
517 while (!kv.eof && !stop);
518 if (onekey || stop > SEARCH_NEXT_KEY)
524 dataiterator_newdata(Dataiterator *di)
526 Id keyname = di->keyname;
527 Repodata *data = di->data;
530 if (data->state == REPODATA_STUB)
535 for (j = 1; j < data->nkeys; j++)
536 if (keyname == data->keys[j].name)
538 if (j == data->nkeys)
542 if (data->loadcallback)
543 data->loadcallback(data);
545 data->state = REPODATA_ERROR;
547 if (data->state == REPODATA_ERROR)
551 unsigned char *dp = data->incoredata + data->incoreoffset[di->solvid - data->start];
552 dp = data_read_id(dp, &schema);
553 Id *keyp = data->schemadata + data->schemata[schema];
557 /* search in a specific key */
558 for (kp = keyp; (k = *kp++) != 0; )
559 if (data->keys[k].name == keyname)
563 dp = forward_to_key(data, k, schema, dp);
573 di->key = di->data->keys + keyid;
578 di->dp = get_data(di->data, di->key, &di->nextkeydp);
583 dataiterator_init(Dataiterator *di, Repo *repo, Id p, Id keyname,
584 const char *match, int flags)
590 di->flags |= __SEARCH_ONESOLVABLE;
591 di->data = repo->repodata - 1;
592 if (flags & SEARCH_NO_STORAGE_SOLVABLE)
599 di->solvid = repo->start - 1;
600 di->data = repo->repodata + repo->nrepodata - 1;
604 di->keyname = keyname;
605 static Id zeroid = 0;
612 /* FIXME factor and merge with repo_matchvalue */
614 dataiterator_match(Dataiterator *di, KeyValue *kv)
616 int flags = di->flags;
618 if ((flags & SEARCH_STRINGMASK) != 0)
620 switch (di->key->type)
622 case REPOKEY_TYPE_ID:
623 case REPOKEY_TYPE_IDARRAY:
624 if (di->data && di->data->localpool)
625 kv->str = stringpool_id2str(&di->data->spool, kv->id);
627 kv->str = id2str(di->repo->pool, kv->id);
629 case REPOKEY_TYPE_STR:
634 switch ((flags & SEARCH_STRINGMASK))
636 case SEARCH_SUBSTRING:
637 if (flags & SEARCH_NOCASE)
639 if (!strcasestr(kv->str, di->match))
644 if (!strstr(kv->str, di->match))
649 if (flags & SEARCH_NOCASE)
651 if (strcasecmp(di->match, kv->str))
656 if (strcmp(di->match, kv->str))
661 if (fnmatch(di->match, kv->str, (flags & SEARCH_NOCASE) ? FNM_CASEFOLD : 0))
666 if (regexec(&di->regexp, kv->str, 0, NULL, 0))
676 static Repokey solvablekeys[RPM_RPMDBID - SOLVABLE_NAME + 1] = {
677 { SOLVABLE_NAME, REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
678 { SOLVABLE_ARCH, REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
679 { SOLVABLE_EVR, REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
680 { SOLVABLE_VENDOR, REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
681 { SOLVABLE_PROVIDES, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
682 { SOLVABLE_OBSOLETES, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
683 { SOLVABLE_CONFLICTS, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
684 { SOLVABLE_REQUIRES, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
685 { SOLVABLE_RECOMMENDS, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
686 { SOLVABLE_SUGGESTS, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
687 { SOLVABLE_SUPPLEMENTS, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
688 { SOLVABLE_ENHANCES, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
689 { SOLVABLE_FRESHENS, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
690 { RPM_RPMDBID, REPOKEY_TYPE_U32, 0, KEY_STORAGE_SOLVABLE },
694 dataiterator_step(Dataiterator *di)
708 di->kv.eof = idp[1] ? 0 : 1;
714 Solvable *s = di->repo->pool->solvables + di->solvid;
715 int state = di->state;
716 di->key = solvablekeys + state - 1;
718 di->state = RPM_RPMDBID;
725 state = di->keyname - 1;
744 case SOLVABLE_VENDOR:
747 di->kv.id = s->vendor;
749 case SOLVABLE_PROVIDES:
750 di->idp = s->provides
751 ? di->repo->idarraydata + s->provides : 0;
753 case SOLVABLE_OBSOLETES:
754 di->idp = s->obsoletes
755 ? di->repo->idarraydata + s->obsoletes : 0;
757 case SOLVABLE_CONFLICTS:
758 di->idp = s->conflicts
759 ? di->repo->idarraydata + s->conflicts : 0;
761 case SOLVABLE_REQUIRES:
762 di->idp = s->requires
763 ? di->repo->idarraydata + s->requires : 0;
765 case SOLVABLE_RECOMMENDS:
766 di->idp = s->recommends
767 ? di->repo->idarraydata + s->recommends : 0;
769 case SOLVABLE_SUPPLEMENTS:
770 di->idp = s->supplements
771 ? di->repo->idarraydata + s->supplements : 0;
773 case SOLVABLE_SUGGESTS:
774 di->idp = s->suggests
775 ? di->repo->idarraydata + s->suggests : 0;
777 case SOLVABLE_ENHANCES:
778 di->idp = s->enhances
779 ? di->repo->idarraydata + s->enhances : 0;
781 case SOLVABLE_FRESHENS:
782 di->idp = s->freshens
783 ? di->repo->idarraydata + s->freshens : 0;
786 if (!di->repo->rpmdbid)
788 di->kv.num = di->repo->rpmdbid[di->solvid - di->repo->start];
791 di->data = di->repo->repodata - 1;
802 di->dp = data_fetch(di->dp, &di->kv, di->key);
807 if (di->keyname || !(keyid = *di->keyp++))
811 Repo *repo = di->repo;
812 Repodata *data = ++di->data;
813 if (data >= repo->repodata + repo->nrepodata)
815 if (di->flags & __SEARCH_ONESOLVABLE)
817 while (++di->solvid < repo->end)
818 if (repo->pool->solvables[di->solvid].repo == repo)
820 if (di->solvid >= repo->end)
822 di->data = repo->repodata - 1;
823 if (di->flags & SEARCH_NO_STORAGE_SOLVABLE)
825 static Id zeroid = 0;
830 if (di->solvid >= data->start && di->solvid < data->end)
832 dataiterator_newdata(di);
840 di->key = di->data->keys + keyid;
841 di->dp = get_data(di->data, di->key, &di->nextkeydp);
843 di->dp = data_fetch(di->dp, &di->kv, di->key);
848 || dataiterator_match(di, &di->kv))
854 /* extend repodata so that it includes solvables p */
856 repodata_extend(Repodata *data, Id p)
858 if (data->start == data->end)
859 data->start = data->end = p;
862 int old = data->end - data->start;
863 int new = p - data->end + 1;
866 data->attrs = sat_extend(data->attrs, old, new, sizeof(Id *), REPODATA_BLOCK);
867 memset(data->attrs + old, 0, new * sizeof(Id *));
869 data->incoreoffset = sat_extend(data->incoreoffset, old, new, sizeof(Id), REPODATA_BLOCK);
870 memset(data->incoreoffset + old, 0, new * sizeof(Id));
875 int old = data->end - data->start;
876 int new = data->start - p;
879 data->attrs = sat_extend_resize(data->attrs, old + new, sizeof(Id *), REPODATA_BLOCK);
880 memmove(data->attrs + new, data->attrs, old * sizeof(Id *));
881 memset(data->attrs, 0, new * sizeof(Id *));
883 data->incoreoffset = sat_extend_resize(data->incoreoffset, old + new, sizeof(Id), REPODATA_BLOCK);
884 memmove(data->incoreoffset + new, data->incoreoffset, old * sizeof(Id));
885 memset(data->incoreoffset, 0, new * sizeof(Id));
891 repodata_extend_block(Repodata *data, Id start, Id num)
895 if (!data->incoreoffset)
897 data->incoreoffset = sat_calloc_block(num, sizeof(Id), REPODATA_BLOCK);
899 data->end = start + num;
902 repodata_extend(data, start);
904 repodata_extend(data, start + num - 1);
907 /**********************************************************************/
909 #define REPODATA_ATTRS_BLOCK 63
910 #define REPODATA_ATTRDATA_BLOCK 1023
911 #define REPODATA_ATTRIDDATA_BLOCK 63
914 repodata_insert_keyid(Repodata *data, Id entry, Id keyid, Id val, int overwrite)
920 data->attrs = sat_calloc_block(data->end - data->start, sizeof(Id *),
924 if (data->attrs[entry])
926 for (pp = data->attrs[entry]; *pp; pp += 2)
927 /* Determine equality based on the name only, allows us to change
928 type (when overwrite is set), and makes TYPE_CONSTANT work. */
929 if (data->keys[*pp].name == data->keys[keyid].name)
940 i = pp - data->attrs[entry];
942 data->attrs[entry] = sat_extend(data->attrs[entry], i, 3, sizeof(Id), REPODATA_ATTRS_BLOCK);
943 pp = data->attrs[entry] + i;
950 repodata_set(Repodata *data, Id entry, Repokey *key, Id val)
954 /* find key in keys */
955 for (keyid = 1; keyid < data->nkeys; keyid++)
956 if (data->keys[keyid].name == key->name && data->keys[keyid].type == key->type)
958 if ((key->type == REPOKEY_TYPE_CONSTANT || key->type == REPOKEY_TYPE_CONSTANTID) && key->size != data->keys[keyid].size)
962 if (keyid == data->nkeys)
964 /* allocate new key */
965 data->keys = sat_realloc2(data->keys, data->nkeys + 1, sizeof(Repokey));
966 data->keys[data->nkeys++] = *key;
967 if (data->verticaloffset)
969 data->verticaloffset = sat_realloc2(data->verticaloffset, data->nkeys, sizeof(Id));
970 data->verticaloffset[data->nkeys - 1] = 0;
973 repodata_insert_keyid(data, entry, keyid, val, 1);
977 repodata_set_id(Repodata *data, Id entry, Id keyname, Id id)
981 key.type = REPOKEY_TYPE_ID;
983 key.storage = KEY_STORAGE_INCORE;
984 repodata_set(data, entry, &key, id);
988 repodata_set_num(Repodata *data, Id entry, Id keyname, Id num)
992 key.type = REPOKEY_TYPE_NUM;
994 key.storage = KEY_STORAGE_INCORE;
995 repodata_set(data, entry, &key, num);
999 repodata_set_poolstr(Repodata *data, Id entry, Id keyname, const char *str)
1003 if (data->localpool)
1004 id = stringpool_str2id(&data->spool, str, 1);
1006 id = str2id(data->repo->pool, str, 1);
1008 key.type = REPOKEY_TYPE_ID;
1010 key.storage = KEY_STORAGE_INCORE;
1011 repodata_set(data, entry, &key, id);
1015 repodata_set_constant(Repodata *data, Id entry, Id keyname, Id constant)
1019 key.type = REPOKEY_TYPE_CONSTANT;
1020 key.size = constant;
1021 key.storage = KEY_STORAGE_INCORE;
1022 repodata_set(data, entry, &key, 0);
1026 repodata_set_constantid(Repodata *data, Id entry, Id keyname, Id id)
1030 key.type = REPOKEY_TYPE_CONSTANTID;
1032 key.storage = KEY_STORAGE_INCORE;
1033 repodata_set(data, entry, &key, 0);
1037 repodata_set_void(Repodata *data, Id entry, Id keyname)
1041 key.type = REPOKEY_TYPE_VOID;
1043 key.storage = KEY_STORAGE_INCORE;
1044 repodata_set(data, entry, &key, 0);
1048 repodata_set_str(Repodata *data, Id entry, Id keyname, const char *str)
1053 l = strlen(str) + 1;
1055 key.type = REPOKEY_TYPE_STR;
1057 key.storage = KEY_STORAGE_INCORE;
1058 data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
1059 memcpy(data->attrdata + data->attrdatalen, str, l);
1060 repodata_set(data, entry, &key, data->attrdatalen);
1061 data->attrdatalen += l;
1065 repoadata_add_array(Repodata *data, Id entry, Id keyname, Id keytype, int entrysize)
1071 if (data->attrs && data->attrs[entry])
1072 for (pp = data->attrs[entry]; *pp; pp += 2)
1073 if (data->keys[*pp].name == keyname && data->keys[*pp].type == keytype)
1077 /* not found. allocate new key */
1082 key.storage = KEY_STORAGE_INCORE;
1083 data->attriddata = sat_extend(data->attriddata, data->attriddatalen, entrysize + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1084 repodata_set(data, entry, &key, data->attriddatalen);
1088 for (ida = data->attriddata + pp[1]; *ida; ida += entrysize)
1089 oldsize += entrysize;
1090 if (ida + 1 == data->attriddata + data->attriddatalen)
1092 /* this was the last entry, just append it */
1093 data->attriddata = sat_extend(data->attriddata, data->attriddatalen, entrysize, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1094 data->attriddatalen--; /* overwrite terminating 0 */
1098 /* too bad. move to back. */
1099 data->attriddata = sat_extend(data->attriddata, data->attriddatalen, oldsize + entrysize + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1100 memcpy(data->attriddata + data->attriddatalen, data->attriddata + pp[1], oldsize * sizeof(Id));
1101 pp[1] = data->attriddatalen;
1102 data->attriddatalen += oldsize;
1107 checksumtype2len(Id type)
1111 case REPOKEY_TYPE_MD5:
1113 case REPOKEY_TYPE_SHA1:
1115 case REPOKEY_TYPE_SHA256:
1116 return SIZEOF_SHA256;
1123 repodata_set_bin_checksum(Repodata *data, Id entry, Id keyname, Id type,
1124 const unsigned char *str)
1127 int l = checksumtype2len(type);
1134 key.storage = KEY_STORAGE_INCORE;
1135 data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
1136 memcpy(data->attrdata + data->attrdatalen, str, l);
1137 repodata_set(data, entry, &key, data->attrdatalen);
1138 data->attrdatalen += l;
1142 hexstr2bytes(unsigned char *buf, const char *str, int buflen)
1145 for (i = 0; i < buflen; i++)
1147 #define c2h(c) (((c)>='0' && (c)<='9') ? ((c)-'0') \
1148 : ((c)>='a' && (c)<='f') ? ((c)-'a'+10) \
1149 : ((c)>='A' && (c)<='F') ? ((c)-'A'+10) \
1160 buf[i] = (buf[i] << 4) | v;
1167 repodata_set_checksum(Repodata *data, Id entry, Id keyname, Id type,
1170 unsigned char buf[64];
1171 int l = checksumtype2len(type);
1175 if (hexstr2bytes(buf, str, l) != l)
1177 fprintf(stderr, "Invalid hex character in '%s'\n", str);
1180 repodata_set_bin_checksum(data, entry, keyname, type, buf);
1184 repodata_chk2str(Repodata *data, Id type, const unsigned char *buf)
1189 l = checksumtype2len(type);
1192 s = str = pool_alloctmpspace(data->repo->pool, 2 * l + 1);
1193 for (i = 0; i < l; i++)
1195 unsigned char v = buf[i];
1196 unsigned char w = v >> 4;
1197 *s++ = w >= 10 ? w + ('a' - 10) : w + '0';
1199 *s++ = w >= 10 ? w + ('a' - 10) : w + '0';
1205 Id repodata_globalize_id(Repodata *data, Id id)
1207 if (!data || !data->localpool)
1209 return str2id(data->repo->pool, stringpool_id2str(&data->spool, id), 1);
1213 repodata_add_dirnumnum(Repodata *data, Id entry, Id keyname, Id dir, Id num, Id num2)
1217 fprintf(stderr, "repodata_add_dirnumnum %d %d %d %d (%d)\n", entry, dir, num, num2, data->attriddatalen);
1219 repoadata_add_array(data, entry, keyname, REPOKEY_TYPE_DIRNUMNUMARRAY, 3);
1220 data->attriddata[data->attriddatalen++] = dir;
1221 data->attriddata[data->attriddatalen++] = num;
1222 data->attriddata[data->attriddatalen++] = num2;
1223 data->attriddata[data->attriddatalen++] = 0;
1227 repodata_add_dirstr(Repodata *data, Id entry, Id keyname, Id dir, const char *str)
1232 l = strlen(str) + 1;
1233 data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
1234 memcpy(data->attrdata + data->attrdatalen, str, l);
1235 stroff = data->attrdatalen;
1236 data->attrdatalen += l;
1239 fprintf(stderr, "repodata_add_dirstr %d %d %s (%d)\n", entry, dir, str, data->attriddatalen);
1241 repoadata_add_array(data, entry, keyname, REPOKEY_TYPE_DIRSTRARRAY, 2);
1242 data->attriddata[data->attriddatalen++] = dir;
1243 data->attriddata[data->attriddatalen++] = stroff;
1244 data->attriddata[data->attriddatalen++] = 0;
1248 repodata_add_idarray(Repodata *data, Id entry, Id keyname, Id id)
1251 fprintf(stderr, "repodata_add_idarray %d %d (%d)\n", entry, id, data->attriddatalen);
1253 repoadata_add_array(data, entry, keyname, REPOKEY_TYPE_IDARRAY, 1);
1254 data->attriddata[data->attriddatalen++] = id;
1255 data->attriddata[data->attriddatalen++] = 0;
1259 repodata_add_poolstr_array(Repodata *data, Id entry, Id keyname,
1263 if (data->localpool)
1264 id = stringpool_str2id(&data->spool, str, 1);
1266 id = str2id(data->repo->pool, str, 1);
1267 repodata_add_idarray(data, entry, keyname, id);
1271 repodata_merge_attrs(Repodata *data, Id dest, Id src)
1274 if (dest == src || !(keyp = data->attrs[src]))
1276 for (; *keyp; keyp += 2)
1277 repodata_insert_keyid(data, dest, keyp[0], keyp[1], 0);
1280 /*********************************/
1282 /* unify with repo_write! */
1284 #define EXTDATA_BLOCK 1023
1285 #define SCHEMATA_BLOCK 31
1286 #define SCHEMATADATA_BLOCK 255
1294 data_addid(struct extdata *xd, Id x)
1297 xd->buf = sat_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
1298 dp = xd->buf + xd->len;
1303 *dp++ = (x >> 28) | 128;
1305 *dp++ = (x >> 21) | 128;
1306 *dp++ = (x >> 14) | 128;
1309 *dp++ = (x >> 7) | 128;
1311 xd->len = dp - xd->buf;
1315 data_addideof(struct extdata *xd, Id x, int eof)
1318 x = (x & 63) | ((x & ~63) << 1);
1319 data_addid(xd, (eof ? x: x | 64));
1323 data_addblob(struct extdata *xd, unsigned char *blob, int len)
1325 xd->buf = sat_extend(xd->buf, xd->len, len, 1, EXTDATA_BLOCK);
1326 memcpy(xd->buf + xd->len, blob, len);
1330 /*********************************/
1333 addschema_prepare(Repodata *data, Id *schematacache)
1338 memset(schematacache, 0, 256 * sizeof(Id));
1339 for (i = 0; i < data->nschemata; i++)
1341 for (sp = data->schemadata + data->schemata[i], h = 0; *sp; len++)
1344 schematacache[h] = i + 1;
1346 data->schemadata = sat_extend_resize(data->schemadata, data->schemadatalen, sizeof(Id), SCHEMATADATA_BLOCK);
1347 data->schemata = sat_extend_resize(data->schemata, data->nschemata, sizeof(Id), SCHEMATA_BLOCK);
1351 addschema(Repodata *data, Id *schema, Id *schematacache)
1356 for (sp = schema, len = 0, h = 0; *sp; len++)
1361 cid = schematacache[h];
1365 if (!memcmp(data->schemadata + data->schemata[cid], schema, len * sizeof(Id)))
1367 /* cache conflict */
1368 for (cid = 0; cid < data->nschemata; cid++)
1369 if (!memcmp(data->schemadata + data->schemata[cid], schema, len * sizeof(Id)))
1372 /* a new one. make room. */
1373 data->schemadata = sat_extend(data->schemadata, data->schemadatalen, len, sizeof(Id), SCHEMATADATA_BLOCK);
1374 data->schemata = sat_extend(data->schemata, data->nschemata, 1, sizeof(Id), SCHEMATA_BLOCK);
1376 memcpy(data->schemadata + data->schemadatalen, schema, len * sizeof(Id));
1377 data->schemata[data->nschemata] = data->schemadatalen;
1378 data->schemadatalen += len;
1379 schematacache[h] = data->nschemata + 1;
1381 fprintf(stderr, "addschema: new schema\n");
1383 return data->nschemata++;
1388 repodata_internalize(Repodata *data)
1391 Id id, entry, nentry, *ida;
1392 Id schematacache[256];
1393 Id schemaid, *schema, *sp, oldschema, *keyp, *seen;
1394 unsigned char *dp, *ndp;
1395 int newschema, oldcount;
1396 struct extdata newincore;
1397 struct extdata newvincore;
1402 newvincore.buf = data->vincore;
1403 newvincore.len = data->vincorelen;
1405 schema = sat_malloc2(data->nkeys, sizeof(Id));
1406 seen = sat_malloc2(data->nkeys, sizeof(Id));
1408 /* Merge the data already existing (in data->schemata, ->incoredata and
1409 friends) with the new attributes in data->attrs[]. */
1410 nentry = data->end - data->start;
1411 addschema_prepare(data, schematacache);
1412 memset(&newincore, 0, sizeof(newincore));
1413 data_addid(&newincore, 0);
1414 for (entry = 0; entry < nentry; entry++)
1416 memset(seen, 0, data->nkeys * sizeof(Id));
1418 dp = data->incoredata + data->incoreoffset[entry];
1419 if (data->incoredata)
1420 dp = data_read_id(dp, &oldschema);
1424 fprintf(stderr, "oldschema %d\n", oldschema);
1425 fprintf(stderr, "schemata %d\n", data->schemata[oldschema]);
1426 fprintf(stderr, "schemadata %p\n", data->schemadata);
1428 /* seen: -1: old data 0: skipped >0: id + 1 */
1431 for (keyp = data->schemadata + data->schemata[oldschema]; *keyp; keyp++)
1435 fprintf(stderr, "Inconsistent old data (key occured twice).\n");
1442 if (data->attrs[entry])
1443 for (keyp = data->attrs[entry]; *keyp; keyp += 2)
1450 seen[*keyp] = keyp[1] + 1;
1454 /* Ideally we'd like to sort the new schema here, to ensure
1455 schema equality independend of the ordering. We can't do that
1456 yet. For once see below (old ids need to come before new ids).
1457 An additional difficulty is that we also need to move
1458 the values with the keys. */
1459 schemaid = addschema(data, schema, schematacache);
1461 schemaid = oldschema;
1464 /* Now create data blob. We walk through the (possibly new) schema
1465 and either copy over old data, or insert the new. */
1466 /* XXX Here we rely on the fact that the (new) schema has the form
1467 o1 o2 o3 o4 ... | n1 n2 n3 ...
1468 (oX being the old keyids (possibly overwritten), and nX being
1469 the new keyids). This rules out sorting the keyids in order
1470 to ensure a small schema count. */
1471 data->incoreoffset[entry] = newincore.len;
1472 data_addid(&newincore, schemaid);
1473 for (keyp = data->schemadata + data->schemata[schemaid]; *keyp; keyp++)
1475 key = data->keys + *keyp;
1479 /* Skip the data associated with this old key. */
1480 if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
1482 ndp = data_skip(dp, REPOKEY_TYPE_ID);
1483 ndp = data_skip(ndp, REPOKEY_TYPE_ID);
1485 else if (key->storage == KEY_STORAGE_INCORE)
1486 ndp = data_skip(dp, key->type);
1489 if (seen[*keyp] == -1)
1491 /* If this key was an old one _and_ was not overwritten with
1492 a different value copy over the old value (we skipped it
1495 data_addblob(&newincore, dp, ndp - dp);
1498 else if (seen[*keyp])
1500 /* Otherwise we have a new value. Parse it into the internal
1503 unsigned int oldvincorelen = 0;
1506 if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
1509 oldvincorelen = xd->len;
1511 id = seen[*keyp] - 1;
1514 case REPOKEY_TYPE_VOID:
1515 case REPOKEY_TYPE_CONSTANT:
1516 case REPOKEY_TYPE_CONSTANTID:
1518 case REPOKEY_TYPE_STR:
1519 data_addblob(xd, data->attrdata + id, strlen((char *)(data->attrdata + id)) + 1);
1521 case REPOKEY_TYPE_MD5:
1522 data_addblob(xd, data->attrdata + id, SIZEOF_MD5);
1524 case REPOKEY_TYPE_SHA1:
1525 data_addblob(xd, data->attrdata + id, SIZEOF_SHA1);
1527 case REPOKEY_TYPE_ID:
1528 case REPOKEY_TYPE_NUM:
1529 case REPOKEY_TYPE_DIR:
1532 case REPOKEY_TYPE_IDARRAY:
1533 for (ida = data->attriddata + id; *ida; ida++)
1534 data_addideof(xd, ida[0], ida[1] ? 0 : 1);
1536 case REPOKEY_TYPE_DIRNUMNUMARRAY:
1537 for (ida = data->attriddata + id; *ida; ida += 3)
1539 data_addid(xd, ida[0]);
1540 data_addid(xd, ida[1]);
1541 data_addideof(xd, ida[2], ida[3] ? 0 : 1);
1544 case REPOKEY_TYPE_DIRSTRARRAY:
1545 for (ida = data->attriddata + id; *ida; ida += 2)
1547 data_addideof(xd, ida[0], ida[2] ? 0 : 1);
1548 data_addblob(xd, data->attrdata + ida[1], strlen((char *)(data->attrdata + ida[1])) + 1);
1552 fprintf(stderr, "don't know how to handle type %d\n", key->type);
1555 if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
1557 /* put offset/len in incore */
1558 data_addid(&newincore, data->lastverticaloffset + oldvincorelen);
1559 oldvincorelen = xd->len - oldvincorelen;
1560 data_addid(&newincore, oldvincorelen);
1565 if (data->attrs[entry])
1566 sat_free(data->attrs[entry]);
1571 sat_free(data->incoredata);
1572 data->incoredata = newincore.buf;
1573 data->incoredatalen = newincore.len;
1574 data->incoredatafree = 0;
1576 sat_free(data->vincore);
1577 data->vincore = newvincore.buf;
1578 data->vincorelen = newvincore.len;
1580 data->attrs = sat_free(data->attrs);
1581 data->attrdata = sat_free(data->attrdata);
1582 data->attriddata = sat_free(data->attriddata);
1583 data->attrdatalen = 0;
1584 data->attriddatalen = 0;
1588 repodata_str2dir(Repodata *data, const char *dir, int create)
1594 while (*dir == '/' && dir[1] == '/')
1596 if (*dir == '/' && !dir[1])
1600 dire = strchrnul(dir, '/');
1601 if (data->localpool)
1602 id = stringpool_strn2id(&data->spool, dir, dire - dir, create);
1604 id = strn2id(data->repo->pool, dir, dire - dir, create);
1607 parent = dirpool_add_dir(&data->dirpool, parent, id, create);
1620 repodata_dir2str(Repodata *data, Id did, const char *suf)
1622 Pool *pool = data->repo->pool;
1629 return suf ? suf : "";
1633 comp = dirpool_compid(&data->dirpool, parent);
1634 comps = stringpool_id2str(data->localpool ? &data->spool : &pool->ss, comp);
1636 parent = dirpool_parent(&data->dirpool, parent);
1641 l += strlen(suf) + 1;
1642 p = pool_alloctmpspace(pool, l + 1) + l;
1653 comp = dirpool_compid(&data->dirpool, parent);
1654 comps = stringpool_id2str(data->localpool ? &data->spool : &pool->ss, comp);
1657 strncpy(p, comps, l);
1658 parent = dirpool_parent(&data->dirpool, parent);
1666 repodata_compress_page(unsigned char *page, unsigned int len, unsigned char *cpage, unsigned int max)
1668 return compress_buf(page, len, cpage, max);
1671 #define SOLV_ERROR_EOF 3
1673 static inline unsigned int
1679 for (i = 0; i < 4; i++)
1689 #define SOLV_ERROR_EOF 3
1690 #define SOLV_ERROR_CORRUPT 6
1692 /* Try to either setup on-demand paging (using FP as backing
1693 file), or in case that doesn't work (FP not seekable) slurps in
1694 all pages and deactivates paging. */
1696 repodata_read_or_setup_pages(Repodata *data, unsigned int pagesz, unsigned int blobsz)
1698 FILE *fp = data->fp;
1699 unsigned int npages;
1701 unsigned int can_seek;
1703 unsigned char buf[BLOB_PAGESIZE];
1705 if (pagesz != BLOB_PAGESIZE)
1707 /* We could handle this by slurping in everything. */
1708 data->error = SOLV_ERROR_CORRUPT;
1712 if ((cur_file_ofs = ftell(fp)) < 0)
1716 data->pagefd = dup(fileno(fp));
1717 if (data->pagefd == -1)
1721 fprintf (stderr, "can %sseek\n", can_seek ? "" : "NOT ");
1723 npages = (blobsz + BLOB_PAGESIZE - 1) / BLOB_PAGESIZE;
1725 data->num_pages = npages;
1726 data->pages = sat_malloc2(npages, sizeof(data->pages[0]));
1728 /* If we can't seek on our input we have to slurp in everything. */
1730 data->blob_store = sat_malloc(npages * BLOB_PAGESIZE);
1731 for (i = 0; i < npages; i++)
1733 unsigned int in_len = read_u32(fp);
1734 unsigned int compressed = in_len & 1;
1735 Attrblobpage *p = data->pages + i;
1738 fprintf (stderr, "page %d: len %d (%scompressed)\n",
1739 i, in_len, compressed ? "" : "not ");
1745 p->file_offset = cur_file_ofs;
1746 p->file_size = in_len * 2 + compressed;
1747 if (fseek(fp, in_len, SEEK_CUR) < 0)
1750 fprintf (stderr, "can't seek after we thought we can\n");
1751 /* We can't fall back to non-seeking behaviour as we already
1752 read over some data pages without storing them away. */
1753 data->error = SOLV_ERROR_EOF;
1754 close(data->pagefd);
1758 cur_file_ofs += in_len;
1762 unsigned int out_len;
1763 void *dest = data->blob_store + i * BLOB_PAGESIZE;
1764 p->mapped_at = i * BLOB_PAGESIZE;
1767 /* We can't seek, so suck everything in. */
1768 if (fread(compressed ? buf : dest, in_len, 1, fp) != 1)
1771 data->error = SOLV_ERROR_EOF;
1776 out_len = unchecked_decompress_buf(buf, in_len, dest, BLOB_PAGESIZE);
1777 if (out_len != BLOB_PAGESIZE && i < npages - 1)
1779 data->error = SOLV_ERROR_CORRUPT;
1788 repodata_disable_paging(Repodata *data)
1790 if (maybe_load_repodata(data, 0)
1792 load_page_range (data, 0, data->num_pages - 1);
1795 vim:cinoptions={.5s,g0,p5,t0,(0,^-0.5s,n-0.5s:tw=78:cindent:sw=4: