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 sat_free(data->location);
70 if (data->pagefd != -1)
74 static unsigned char *
75 forward_to_key(Repodata *data, Id key, Id schemaid, unsigned char *dp)
79 keyp = data->schemadata + data->schemata[schemaid];
80 while ((k = *keyp++) != 0)
84 if (data->keys[k].storage == KEY_STORAGE_VERTICAL_OFFSET)
86 dp = data_skip(dp, REPOKEY_TYPE_ID); /* skip that offset */
87 dp = data_skip(dp, REPOKEY_TYPE_ID); /* skip that length */
90 if (data->keys[k].storage != KEY_STORAGE_INCORE)
92 dp = data_skip(dp, data->keys[k].type);
97 #define BLOB_PAGEBITS 15
98 #define BLOB_PAGESIZE (1 << BLOB_PAGEBITS)
100 static unsigned char *
101 load_page_range(Repodata *data, unsigned int pstart, unsigned int pend)
103 /* Make sure all pages from PSTART to PEND (inclusive) are loaded,
104 and are consecutive. Return a pointer to the mapping of PSTART. */
105 unsigned char buf[BLOB_PAGESIZE];
108 /* Quick check in case all pages are there already and consecutive. */
109 for (i = pstart; i <= pend; i++)
110 if (data->pages[i].mapped_at == -1
112 && data->pages[i].mapped_at
113 != data->pages[i-1].mapped_at + BLOB_PAGESIZE))
116 return data->blob_store + data->pages[pstart].mapped_at;
118 if (data->pagefd == -1)
121 /* Ensure that we can map the numbers of pages we need at all. */
122 if (pend - pstart + 1 > data->ncanmap)
124 unsigned int oldcan = data->ncanmap;
125 data->ncanmap = pend - pstart + 1;
126 if (data->ncanmap < 4)
128 data->mapped = sat_realloc2(data->mapped, data->ncanmap, sizeof(data->mapped[0]));
129 memset (data->mapped + oldcan, 0, (data->ncanmap - oldcan) * sizeof (data->mapped[0]));
130 data->blob_store = sat_realloc2(data->blob_store, data->ncanmap, BLOB_PAGESIZE);
132 fprintf (stderr, "PAGE: can map %d pages\n", data->ncanmap);
136 /* Now search for "cheap" space in our store. Space is cheap if it's either
137 free (very cheap) or contains pages we search for anyway. */
139 /* Setup cost array. */
140 unsigned int cost[data->ncanmap];
141 for (i = 0; i < data->ncanmap; i++)
143 unsigned int pnum = data->mapped[i];
149 Attrblobpage *p = data->pages + pnum;
150 assert (p->mapped_at != -1);
151 if (pnum >= pstart && pnum <= pend)
158 /* And search for cheapest space. */
159 unsigned int best_cost = -1;
160 unsigned int best = 0;
161 unsigned int same_cost = 0;
162 for (i = 0; i + pend - pstart < data->ncanmap; i++)
164 unsigned int c = cost[i];
166 for (j = 0; j < pend - pstart + 1; j++)
169 best_cost = c, best = i;
170 else if (c == best_cost)
172 /* A null cost won't become better. */
176 /* If all places have the same cost we would thrash on slot 0. Avoid
177 this by doing a round-robin strategy in this case. */
178 if (same_cost == data->ncanmap - pend + pstart - 1)
179 best = data->rr_counter++ % (data->ncanmap - pend + pstart);
181 /* So we want to map our pages from [best] to [best+pend-pstart].
182 Use a very simple strategy, which doesn't make the best use of
183 our resources, but works. Throw away all pages in that range
184 (even ours) then copy around ours (in case they were outside the
185 range) or read them in. */
186 for (i = best; i < best + pend - pstart + 1; i++)
188 unsigned int pnum = data->mapped[i];
190 /* If this page is exactly at the right place already,
191 no need to evict it. */
192 && pnum != pstart + i - best)
194 /* Evict this page. */
196 fprintf (stderr, "PAGE: evict page %d from %d\n", pnum, i);
200 data->pages[pnum].mapped_at = -1;
204 /* Everything is free now. Read in the pages we want. */
205 for (i = pstart; i <= pend; i++)
207 Attrblobpage *p = data->pages + i;
208 unsigned int pnum = i - pstart + best;
209 void *dest = data->blob_store + pnum * BLOB_PAGESIZE;
210 if (p->mapped_at != -1)
212 if (p->mapped_at != pnum * BLOB_PAGESIZE)
215 fprintf (stderr, "PAGECOPY: %d to %d\n", i, pnum);
217 /* Still mapped somewhere else, so just copy it from there. */
218 memcpy (dest, data->blob_store + p->mapped_at, BLOB_PAGESIZE);
219 data->mapped[p->mapped_at / BLOB_PAGESIZE] = 0;
224 unsigned int in_len = p->file_size;
225 unsigned int compressed = in_len & 1;
228 fprintf (stderr, "PAGEIN: %d to %d", i, pnum);
230 if (pread(data->pagefd, compressed ? buf : dest, in_len, p->file_offset) != in_len)
232 perror ("mapping pread");
237 unsigned int out_len;
238 out_len = unchecked_decompress_buf(buf, in_len,
239 dest, BLOB_PAGESIZE);
240 if (out_len != BLOB_PAGESIZE && i < data->num_pages - 1)
242 fprintf(stderr, "can't decompress\n");
246 fprintf (stderr, " (expand %d to %d)", in_len, out_len);
250 fprintf (stderr, "\n");
253 p->mapped_at = pnum * BLOB_PAGESIZE;
254 data->mapped[pnum] = i + 1;
256 return data->blob_store + best * BLOB_PAGESIZE;
259 static unsigned char *
260 make_vertical_available(Repodata *data, Repokey *key, Id off, Id len)
263 if (key->type == REPOKEY_TYPE_VOID)
265 if (off >= data->lastverticaloffset)
267 off -= data->lastverticaloffset;
268 if (off + len > data->vincorelen)
270 return data->vincore + off;
272 if (off + len > key->size)
274 /* we now have the offset, go into vertical */
275 off += data->verticaloffset[key - data->keys];
276 dp = load_page_range(data, off / BLOB_PAGESIZE, (off + len - 1) / BLOB_PAGESIZE);
278 dp += off % BLOB_PAGESIZE;
282 static inline unsigned char *
283 get_data(Repodata *data, Repokey *key, unsigned char **dpp)
285 unsigned char *dp = *dpp;
289 if (key->storage == KEY_STORAGE_INCORE)
291 /* hmm, this is a bit expensive */
292 *dpp = data_skip(dp, key->type);
295 else if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
298 dp = data_read_id(dp, &off);
299 dp = data_read_id(dp, &len);
301 return make_vertical_available(data, key, off, len);
307 maybe_load_repodata(Repodata *data, Id *keyid)
309 if (data->state == REPODATA_STUB)
311 if (data->loadcallback)
315 /* key order may change when loading */
317 Id name = data->keys[*keyid].name;
318 Id type = data->keys[*keyid].type;
319 data->loadcallback(data);
320 if (data->state == REPODATA_AVAILABLE)
322 for (i = 1; i < data->nkeys; i++)
323 if (data->keys[i].name == name && data->keys[i].type == type)
332 data->loadcallback(data);
335 data->state = REPODATA_ERROR;
337 if (data->state == REPODATA_AVAILABLE)
339 data->state = REPODATA_ERROR;
344 repodata_lookup_str(Repodata *data, Id entry, Id keyid)
351 if (!maybe_load_repodata(data, &keyid))
354 dp = data->incoredata + data->incoreoffset[entry];
355 dp = data_read_id(dp, &schema);
356 /* make sure the schema of this solvable contains the key */
357 for (keyp = data->schemadata + data->schemata[schema]; *keyp != keyid; keyp++)
360 dp = forward_to_key(data, keyid, schema, dp);
361 key = data->keys + keyid;
362 dp = get_data(data, key, &dp);
365 if (key->type == REPOKEY_TYPE_STR)
366 return (const char *)dp;
367 if (key->type == REPOKEY_TYPE_CONSTANTID)
368 return id2str(data->repo->pool, key->size);
369 if (key->type == REPOKEY_TYPE_ID)
370 dp = data_read_id(dp, &id);
374 return data->spool.stringspace + data->spool.strings[id];
375 return id2str(data->repo->pool, id);
379 repodata_lookup_num(Repodata *data, Id entry, Id keyid, unsigned int *value)
389 if (!maybe_load_repodata(data, &keyid))
392 dp = data->incoredata + data->incoreoffset[entry];
393 dp = data_read_id(dp, &schema);
394 /* make sure the schema of this solvable contains the key */
395 for (keyp = data->schemadata + data->schemata[schema]; *keyp != keyid; keyp++)
398 dp = forward_to_key(data, keyid, schema, dp);
399 key = data->keys + keyid;
400 dp = get_data(data, key, &dp);
403 if (key->type == REPOKEY_TYPE_NUM
404 || key->type == REPOKEY_TYPE_U32
405 || key->type == REPOKEY_TYPE_CONSTANT)
407 dp = data_fetch(dp, &kv, key);
415 repodata_lookup_void(Repodata *data, Id entry, Id keyid)
420 if (!maybe_load_repodata(data, &keyid))
422 dp = data->incoredata + data->incoreoffset[entry];
423 dp = data_read_id(dp, &schema);
424 for (keyp = data->schemadata + data->schemata[schema]; *keyp != keyid; keyp++)
431 repodata_search(Repodata *data, Id entry, Id keyname, int (*callback)(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv), void *cbdata)
435 Id k, keyid, *kp, *keyp;
436 unsigned char *dp, *ddp;
441 if (!maybe_load_repodata(data, 0))
444 dp = data->incoredata + data->incoreoffset[entry];
445 dp = data_read_id(dp, &schema);
446 keyp = data->schemadata + data->schemata[schema];
449 /* search in a specific key */
450 for (kp = keyp; (k = *kp++) != 0; )
451 if (data->keys[k].name == keyname)
455 dp = forward_to_key(data, k, schema, dp);
461 while ((keyid = *keyp++) != 0)
464 key = data->keys + keyid;
465 ddp = get_data(data, key, &dp);
468 ddp = data_fetch(ddp, &kv, key);
471 stop = callback(cbdata, data->repo->pool->solvables + data->start + entry, data, key, &kv);
473 while (!kv.eof && !stop);
474 if (onekey || stop > SEARCH_NEXT_KEY)
480 dataiterator_newdata(Dataiterator *di)
482 Id keyname = di->keyname;
483 Repodata *data = di->data;
486 if (data->state == REPODATA_STUB)
491 for (j = 1; j < data->nkeys; j++)
492 if (keyname == data->keys[j].name)
494 if (j == data->nkeys)
498 if (data->loadcallback)
499 data->loadcallback(data);
501 data->state = REPODATA_ERROR;
503 if (data->state == REPODATA_ERROR)
507 unsigned char *dp = data->incoredata + data->incoreoffset[di->solvid - data->start];
508 dp = data_read_id(dp, &schema);
509 Id *keyp = data->schemadata + data->schemata[schema];
513 /* search in a specific key */
514 for (kp = keyp; (k = *kp++) != 0; )
515 if (data->keys[k].name == keyname)
519 dp = forward_to_key(data, k, schema, dp);
529 di->key = di->data->keys + keyid;
534 di->dp = get_data(di->data, di->key, &di->nextkeydp);
539 dataiterator_init(Dataiterator *di, Repo *repo, Id p, Id keyname,
540 const char *match, int flags)
546 di->flags |= __SEARCH_ONESOLVABLE;
547 di->data = repo->repodata - 1;
548 if (flags & SEARCH_NO_STORAGE_SOLVABLE)
555 di->solvid = repo->start - 1;
556 di->data = repo->repodata + repo->nrepodata - 1;
560 di->keyname = keyname;
561 static Id zeroid = 0;
568 /* FIXME factor and merge with repo_matchvalue */
570 dataiterator_match(Dataiterator *di, KeyValue *kv)
572 int flags = di->flags;
574 if ((flags & SEARCH_STRINGMASK) != 0)
576 switch (di->key->type)
578 case REPOKEY_TYPE_ID:
579 case REPOKEY_TYPE_IDARRAY:
580 if (di->data && di->data->localpool)
581 kv->str = stringpool_id2str(&di->data->spool, kv->id);
583 kv->str = id2str(di->repo->pool, kv->id);
585 case REPOKEY_TYPE_STR:
590 switch ((flags & SEARCH_STRINGMASK))
592 case SEARCH_SUBSTRING:
593 if (flags & SEARCH_NOCASE)
595 if (!strcasestr(kv->str, di->match))
600 if (!strstr(kv->str, di->match))
605 if (flags & SEARCH_NOCASE)
607 if (strcasecmp(di->match, kv->str))
612 if (strcmp(di->match, kv->str))
617 if (fnmatch(di->match, kv->str, (flags & SEARCH_NOCASE) ? FNM_CASEFOLD : 0))
622 if (regexec(&di->regexp, kv->str, 0, NULL, 0))
632 static Repokey solvablekeys[RPM_RPMDBID - SOLVABLE_NAME + 1] = {
633 { SOLVABLE_NAME, REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
634 { SOLVABLE_ARCH, REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
635 { SOLVABLE_EVR, REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
636 { SOLVABLE_VENDOR, REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
637 { SOLVABLE_PROVIDES, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
638 { SOLVABLE_OBSOLETES, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
639 { SOLVABLE_CONFLICTS, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
640 { SOLVABLE_REQUIRES, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
641 { SOLVABLE_RECOMMENDS, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
642 { SOLVABLE_SUGGESTS, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
643 { SOLVABLE_SUPPLEMENTS, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
644 { SOLVABLE_ENHANCES, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
645 { SOLVABLE_FRESHENS, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
646 { RPM_RPMDBID, REPOKEY_TYPE_U32, 0, KEY_STORAGE_SOLVABLE },
650 dataiterator_step(Dataiterator *di)
664 di->kv.eof = idp[1] ? 0 : 1;
670 Solvable *s = di->repo->pool->solvables + di->solvid;
671 int state = di->state;
672 di->key = solvablekeys + state - 1;
674 di->state = RPM_RPMDBID;
681 state = di->keyname - 1;
700 case SOLVABLE_VENDOR:
703 di->kv.id = s->vendor;
705 case SOLVABLE_PROVIDES:
706 di->idp = s->provides
707 ? di->repo->idarraydata + s->provides : 0;
709 case SOLVABLE_OBSOLETES:
710 di->idp = s->obsoletes
711 ? di->repo->idarraydata + s->obsoletes : 0;
713 case SOLVABLE_CONFLICTS:
714 di->idp = s->conflicts
715 ? di->repo->idarraydata + s->conflicts : 0;
717 case SOLVABLE_REQUIRES:
718 di->idp = s->requires
719 ? di->repo->idarraydata + s->requires : 0;
721 case SOLVABLE_RECOMMENDS:
722 di->idp = s->recommends
723 ? di->repo->idarraydata + s->recommends : 0;
725 case SOLVABLE_SUPPLEMENTS:
726 di->idp = s->supplements
727 ? di->repo->idarraydata + s->supplements : 0;
729 case SOLVABLE_SUGGESTS:
730 di->idp = s->suggests
731 ? di->repo->idarraydata + s->suggests : 0;
733 case SOLVABLE_ENHANCES:
734 di->idp = s->enhances
735 ? di->repo->idarraydata + s->enhances : 0;
737 case SOLVABLE_FRESHENS:
738 di->idp = s->freshens
739 ? di->repo->idarraydata + s->freshens : 0;
742 if (!di->repo->rpmdbid)
744 di->kv.num = di->repo->rpmdbid[di->solvid - di->repo->start];
747 di->data = di->repo->repodata - 1;
758 di->dp = data_fetch(di->dp, &di->kv, di->key);
763 if (di->keyname || !(keyid = *di->keyp++))
767 Repo *repo = di->repo;
768 Repodata *data = ++di->data;
769 if (data >= repo->repodata + repo->nrepodata)
771 if (di->flags & __SEARCH_ONESOLVABLE)
773 while (++di->solvid < repo->end)
774 if (repo->pool->solvables[di->solvid].repo == repo)
776 if (di->solvid >= repo->end)
778 di->data = repo->repodata - 1;
779 if (di->flags & SEARCH_NO_STORAGE_SOLVABLE)
781 static Id zeroid = 0;
786 if (di->solvid >= data->start && di->solvid < data->end)
788 dataiterator_newdata(di);
796 di->key = di->data->keys + keyid;
797 di->dp = get_data(di->data, di->key, &di->nextkeydp);
799 di->dp = data_fetch(di->dp, &di->kv, di->key);
804 || dataiterator_match(di, &di->kv))
811 repodata_init(Repodata *data, Repo *repo, int localpool)
813 memset(data, 0, sizeof (*data));
815 data->localpool = localpool;
817 stringpool_init_empty(&data->spool);
818 data->keys = sat_calloc(1, sizeof(Repokey));
820 data->schemata = sat_calloc(1, sizeof(Id));
821 data->schemadata = sat_calloc(1, sizeof(Id));
823 data->schemadatalen = 1;
824 data->start = repo->start;
825 data->end = repo->end;
826 data->incoreoffset = sat_extend_resize(0, data->end - data->start, sizeof(Id), REPODATA_BLOCK);
830 /* extend repodata so that it includes solvables p */
832 repodata_extend(Repodata *data, Id p)
834 if (data->start == data->end)
835 data->start = data->end = p;
838 int old = data->end - data->start;
839 int new = p - data->end + 1;
842 data->attrs = sat_extend(data->attrs, old, new, sizeof(Id *), REPODATA_BLOCK);
843 memset(data->attrs + old, 0, new * sizeof(Id *));
845 data->incoreoffset = sat_extend(data->incoreoffset, old, new, sizeof(Id), REPODATA_BLOCK);
846 memset(data->incoreoffset + old, 0, new * sizeof(Id));
851 int old = data->end - data->start;
852 int new = data->start - p;
855 data->attrs = sat_extend_resize(data->attrs, old + new, sizeof(Id *), REPODATA_BLOCK);
856 memmove(data->attrs + new, data->attrs, old * sizeof(Id *));
857 memset(data->attrs, 0, new * sizeof(Id *));
859 data->incoreoffset = sat_extend_resize(data->incoreoffset, old + new, sizeof(Id), REPODATA_BLOCK);
860 memmove(data->incoreoffset + new, data->incoreoffset, old * sizeof(Id));
861 memset(data->incoreoffset, 0, new * sizeof(Id));
867 repodata_extend_block(Repodata *data, Id start, Id num)
871 if (!data->incoreoffset)
873 data->incoreoffset = sat_extend_resize(data->incoreoffset, num, sizeof(Id), REPODATA_BLOCK);
874 memset(data->incoreoffset, 0, num * sizeof(Id));
876 data->end = start + num;
879 repodata_extend(data, start);
881 repodata_extend(data, start + num - 1);
884 #define REPODATA_ATTRS_BLOCK 63
885 #define REPODATA_ATTRDATA_BLOCK 1023
886 #define REPODATA_ATTRIDDATA_BLOCK 63
889 repodata_insert_keyid(Repodata *data, Id entry, Id keyid, Id val, int overwrite)
895 data->attrs = sat_extend_resize(0, data->end - data->start, sizeof(Id *), REPODATA_BLOCK);
896 memset(data->attrs, 0, (data->end - data->start) * sizeof(Id *));
899 if (data->attrs[entry])
901 for (pp = data->attrs[entry]; *pp; pp += 2)
902 /* Determine equality based on the name only, allows us to change
903 type (when overwrite is set), and makes TYPE_CONSTANT work. */
904 if (data->keys[*pp].name == data->keys[keyid].name)
915 i = pp - data->attrs[entry];
917 data->attrs[entry] = sat_extend(data->attrs[entry], i, 3, sizeof(Id), REPODATA_ATTRS_BLOCK);
918 pp = data->attrs[entry] + i;
925 repodata_set(Repodata *data, Id entry, Repokey *key, Id val)
929 /* find key in keys */
930 for (keyid = 1; keyid < data->nkeys; keyid++)
931 if (data->keys[keyid].name == key->name && data->keys[keyid].type == key->type)
933 if ((key->type == REPOKEY_TYPE_CONSTANT || key->type == REPOKEY_TYPE_CONSTANTID) && key->size != data->keys[keyid].size)
937 if (keyid == data->nkeys)
939 /* allocate new key */
940 data->keys = sat_realloc2(data->keys, data->nkeys + 1, sizeof(Repokey));
941 data->keys[data->nkeys++] = *key;
942 if (data->verticaloffset)
944 data->verticaloffset = sat_realloc2(data->verticaloffset, data->nkeys, sizeof(Id));
945 data->verticaloffset[data->nkeys - 1] = 0;
948 repodata_insert_keyid(data, entry, keyid, val, 1);
952 repodata_set_id(Repodata *data, Id entry, Id keyname, Id id)
956 key.type = REPOKEY_TYPE_ID;
958 key.storage = KEY_STORAGE_INCORE;
959 repodata_set(data, entry, &key, id);
963 repodata_set_num(Repodata *data, Id entry, Id keyname, Id num)
967 key.type = REPOKEY_TYPE_NUM;
969 key.storage = KEY_STORAGE_INCORE;
970 repodata_set(data, entry, &key, num);
974 repodata_set_poolstr(Repodata *data, Id entry, Id keyname, const char *str)
979 id = stringpool_str2id(&data->spool, str, 1);
981 id = str2id(data->repo->pool, str, 1);
983 key.type = REPOKEY_TYPE_ID;
985 key.storage = KEY_STORAGE_INCORE;
986 repodata_set(data, entry, &key, id);
990 repodata_set_constant(Repodata *data, Id entry, Id keyname, Id constant)
994 key.type = REPOKEY_TYPE_CONSTANT;
996 key.storage = KEY_STORAGE_INCORE;
997 repodata_set(data, entry, &key, 0);
1001 repodata_set_constantid(Repodata *data, Id entry, Id keyname, Id id)
1005 key.type = REPOKEY_TYPE_CONSTANTID;
1007 key.storage = KEY_STORAGE_INCORE;
1008 repodata_set(data, entry, &key, 0);
1012 repodata_set_void(Repodata *data, Id entry, Id keyname)
1016 key.type = REPOKEY_TYPE_VOID;
1018 key.storage = KEY_STORAGE_INCORE;
1019 repodata_set(data, entry, &key, 0);
1023 repodata_set_str(Repodata *data, Id entry, Id keyname, const char *str)
1028 l = strlen(str) + 1;
1030 key.type = REPOKEY_TYPE_STR;
1032 key.storage = KEY_STORAGE_INCORE;
1033 data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
1034 memcpy(data->attrdata + data->attrdatalen, str, l);
1035 repodata_set(data, entry, &key, data->attrdatalen);
1036 data->attrdatalen += l;
1040 repodata_add_dirnumnum(Repodata *data, Id entry, Id keyname, Id dir, Id num, Id num2)
1046 fprintf(stderr, "repodata_add_dirnumnum %d %d %d %d (%d)\n", entry, dir, num, num2, data->attriddatalen);
1048 if (data->attrs && data->attrs[entry])
1050 for (pp = data->attrs[entry]; *pp; pp += 2)
1051 if (data->keys[*pp].name == keyname && data->keys[*pp].type == REPOKEY_TYPE_DIRNUMNUMARRAY)
1056 for (ida = data->attriddata + pp[1]; *ida; ida += 3)
1058 if (ida + 1 == data->attriddata + data->attriddatalen)
1060 /* this was the last entry, just append it */
1061 data->attriddata = sat_extend(data->attriddata, data->attriddatalen, 3, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1062 data->attriddatalen--; /* overwrite terminating 0 */
1066 /* too bad. move to back. */
1067 data->attriddata = sat_extend(data->attriddata, data->attriddatalen, oldsize + 4, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1068 memcpy(data->attriddata + data->attriddatalen, data->attriddata + pp[1], oldsize * sizeof(Id));
1069 pp[1] = data->attriddatalen;
1070 data->attriddatalen += oldsize;
1072 data->attriddata[data->attriddatalen++] = dir;
1073 data->attriddata[data->attriddatalen++] = num;
1074 data->attriddata[data->attriddatalen++] = num2;
1075 data->attriddata[data->attriddatalen++] = 0;
1080 key.type = REPOKEY_TYPE_DIRNUMNUMARRAY;
1082 key.storage = KEY_STORAGE_INCORE;
1083 data->attriddata = sat_extend(data->attriddata, data->attriddatalen, 4, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1084 repodata_set(data, entry, &key, data->attriddatalen);
1085 data->attriddata[data->attriddatalen++] = dir;
1086 data->attriddata[data->attriddatalen++] = num;
1087 data->attriddata[data->attriddatalen++] = num2;
1088 data->attriddata[data->attriddatalen++] = 0;
1092 repodata_add_dirstr(Repodata *data, Id entry, Id keyname, Id dir, const char *str)
1094 Id *ida, *pp, stroff;
1098 l = strlen(str) + 1;
1099 data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
1100 memcpy(data->attrdata + data->attrdatalen, str, l);
1101 stroff = data->attrdatalen;
1102 data->attrdatalen += l;
1105 fprintf(stderr, "repodata_add_dirstr %d %d %s (%d)\n", entry, dir, str, data->attriddatalen);
1107 if (data->attrs && data->attrs[entry])
1109 for (pp = data->attrs[entry]; *pp; pp += 2)
1110 if (data->keys[*pp].name == keyname && data->keys[*pp].type == REPOKEY_TYPE_DIRSTRARRAY)
1115 for (ida = data->attriddata + pp[1]; *ida; ida += 2)
1117 if (ida + 1 == data->attriddata + data->attriddatalen)
1119 /* this was the last entry, just append it */
1120 data->attriddata = sat_extend(data->attriddata, data->attriddatalen, 2, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1121 data->attriddatalen--; /* overwrite terminating 0 */
1125 /* too bad. move to back. */
1126 data->attriddata = sat_extend(data->attriddata, data->attriddatalen, oldsize + 3, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1127 memcpy(data->attriddata + data->attriddatalen, data->attriddata + pp[1], oldsize * sizeof(Id));
1128 pp[1] = data->attriddatalen;
1129 data->attriddatalen += oldsize;
1131 data->attriddata[data->attriddatalen++] = dir;
1132 data->attriddata[data->attriddatalen++] = stroff;
1133 data->attriddata[data->attriddatalen++] = 0;
1138 key.type = REPOKEY_TYPE_DIRSTRARRAY;
1140 key.storage = KEY_STORAGE_INCORE;
1141 data->attriddata = sat_extend(data->attriddata, data->attriddatalen, 3, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1142 repodata_set(data, entry, &key, data->attriddatalen);
1143 data->attriddata[data->attriddatalen++] = dir;
1144 data->attriddata[data->attriddatalen++] = stroff;
1145 data->attriddata[data->attriddatalen++] = 0;
1149 repodata_merge_attrs(Repodata *data, Id dest, Id src)
1152 if (dest == src || !(keyp = data->attrs[src]))
1154 for (; *keyp; keyp += 2)
1155 repodata_insert_keyid(data, dest, keyp[0], keyp[1], 0);
1158 /*********************************/
1160 /* unify with repo_write! */
1162 #define EXTDATA_BLOCK 1023
1163 #define SCHEMATA_BLOCK 31
1164 #define SCHEMATADATA_BLOCK 255
1172 data_addid(struct extdata *xd, Id x)
1175 xd->buf = sat_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
1176 dp = xd->buf + xd->len;
1181 *dp++ = (x >> 28) | 128;
1183 *dp++ = (x >> 21) | 128;
1184 *dp++ = (x >> 14) | 128;
1187 *dp++ = (x >> 7) | 128;
1189 xd->len = dp - xd->buf;
1193 data_addideof(struct extdata *xd, Id x, int eof)
1196 x = (x & 63) | ((x & ~63) << 1);
1197 data_addid(xd, (eof ? x: x | 64));
1201 data_addblob(struct extdata *xd, unsigned char *blob, int len)
1203 xd->buf = sat_extend(xd->buf, xd->len, len, 1, EXTDATA_BLOCK);
1204 memcpy(xd->buf + xd->len, blob, len);
1208 /*********************************/
1211 addschema_prepare(Repodata *data, Id *schematacache)
1216 memset(schematacache, 0, 256 * sizeof(Id));
1217 for (i = 0; i < data->nschemata; i++)
1219 for (sp = data->schemadata + data->schemata[i], h = 0; *sp; len++)
1222 schematacache[h] = i + 1;
1224 data->schemadata = sat_extend_resize(data->schemadata, data->schemadatalen, sizeof(Id), SCHEMATADATA_BLOCK);
1225 data->schemata = sat_extend_resize(data->schemata, data->nschemata, sizeof(Id), SCHEMATA_BLOCK);
1229 addschema(Repodata *data, Id *schema, Id *schematacache)
1234 for (sp = schema, len = 0, h = 0; *sp; len++)
1239 cid = schematacache[h];
1243 if (!memcmp(data->schemadata + data->schemata[cid], schema, len * sizeof(Id)))
1245 /* cache conflict */
1246 for (cid = 0; cid < data->nschemata; cid++)
1247 if (!memcmp(data->schemadata + data->schemata[cid], schema, len * sizeof(Id)))
1250 /* a new one. make room. */
1251 data->schemadata = sat_extend(data->schemadata, data->schemadatalen, len, sizeof(Id), SCHEMATADATA_BLOCK);
1252 data->schemata = sat_extend(data->schemata, data->nschemata, 1, sizeof(Id), SCHEMATA_BLOCK);
1254 memcpy(data->schemadata + data->schemadatalen, schema, len * sizeof(Id));
1255 data->schemata[data->nschemata] = data->schemadatalen;
1256 data->schemadatalen += len;
1257 schematacache[h] = data->nschemata + 1;
1259 fprintf(stderr, "addschema: new schema\n");
1261 return data->nschemata++;
1266 repodata_internalize(Repodata *data)
1269 Id id, entry, nentry, *ida;
1270 Id schematacache[256];
1271 Id schemaid, *schema, *sp, oldschema, *keyp, *seen;
1272 unsigned char *dp, *ndp;
1273 int newschema, oldcount;
1274 struct extdata newincore;
1275 struct extdata newvincore;
1280 newvincore.buf = data->vincore;
1281 newvincore.len = data->vincorelen;
1283 schema = sat_malloc2(data->nkeys, sizeof(Id));
1284 seen = sat_malloc2(data->nkeys, sizeof(Id));
1286 /* Merge the data already existing (in data->schemata, ->incoredata and
1287 friends) with the new attributes in data->attrs[]. */
1288 nentry = data->end - data->start;
1289 addschema_prepare(data, schematacache);
1290 memset(&newincore, 0, sizeof(newincore));
1291 for (entry = 0; entry < nentry; entry++)
1293 memset(seen, 0, data->nkeys * sizeof(Id));
1295 dp = data->incoredata + data->incoreoffset[entry];
1296 if (data->incoredata)
1297 dp = data_read_id(dp, &oldschema);
1301 fprintf(stderr, "oldschema %d\n", oldschema);
1302 fprintf(stderr, "schemata %d\n", data->schemata[oldschema]);
1303 fprintf(stderr, "schemadata %p\n", data->schemadata);
1305 /* seen: -1: old data 0: skipped >0: id + 1 */
1308 for (keyp = data->schemadata + data->schemata[oldschema]; *keyp; keyp++)
1312 fprintf(stderr, "Inconsistent old data (key occured twice).\n");
1319 if (data->attrs[entry])
1320 for (keyp = data->attrs[entry]; *keyp; keyp += 2)
1327 seen[*keyp] = keyp[1] + 1;
1331 /* Ideally we'd like to sort the new schema here, to ensure
1332 schema equality independend of the ordering. We can't do that
1333 yet. For once see below (old ids need to come before new ids).
1334 An additional difficulty is that we also need to move
1335 the values with the keys. */
1336 schemaid = addschema(data, schema, schematacache);
1338 schemaid = oldschema;
1341 /* Now create data blob. We walk through the (possibly new) schema
1342 and either copy over old data, or insert the new. */
1343 /* XXX Here we rely on the fact that the (new) schema has the form
1344 o1 o2 o3 o4 ... | n1 n2 n3 ...
1345 (oX being the old keyids (possibly overwritten), and nX being
1346 the new keyids). This rules out sorting the keyids in order
1347 to ensure a small schema count. */
1348 data->incoreoffset[entry] = newincore.len;
1349 data_addid(&newincore, schemaid);
1350 for (keyp = data->schemadata + data->schemata[schemaid]; *keyp; keyp++)
1352 key = data->keys + *keyp;
1356 /* Skip the data associated with this old key. */
1357 if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
1359 ndp = data_skip(dp, REPOKEY_TYPE_ID);
1360 ndp = data_skip(ndp, REPOKEY_TYPE_ID);
1362 else if (key->storage == KEY_STORAGE_INCORE)
1363 ndp = data_skip(dp, key->type);
1366 if (seen[*keyp] == -1)
1368 /* If this key was an old one _and_ was not overwritten with
1369 a different value copy over the old value (we skipped it
1372 data_addblob(&newincore, dp, ndp - dp);
1375 else if (seen[*keyp])
1377 /* Otherwise we have a new value. Parse it into the internal
1380 unsigned int oldvincorelen = 0;
1383 if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
1386 oldvincorelen = xd->len;
1388 id = seen[*keyp] - 1;
1391 case REPOKEY_TYPE_VOID:
1392 case REPOKEY_TYPE_CONSTANT:
1393 case REPOKEY_TYPE_CONSTANTID:
1395 case REPOKEY_TYPE_STR:
1396 data_addblob(xd, data->attrdata + id, strlen((char *)(data->attrdata + id)) + 1);
1398 case REPOKEY_TYPE_ID:
1399 case REPOKEY_TYPE_NUM:
1400 case REPOKEY_TYPE_DIR:
1403 case REPOKEY_TYPE_DIRNUMNUMARRAY:
1404 for (ida = data->attriddata + id; *ida; ida += 3)
1406 data_addid(xd, ida[0]);
1407 data_addid(xd, ida[1]);
1408 data_addideof(xd, ida[2], ida[3] ? 0 : 1);
1411 case REPOKEY_TYPE_DIRSTRARRAY:
1412 for (ida = data->attriddata + id; *ida; ida += 2)
1414 data_addideof(xd, ida[0], ida[2] ? 0 : 1);
1415 data_addblob(xd, data->attrdata + ida[1], strlen((char *)(data->attrdata + ida[1])) + 1);
1419 fprintf(stderr, "don't know how to handle type %d\n", key->type);
1422 if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
1424 /* put offset/len in incore */
1425 data_addid(&newincore, data->lastverticaloffset + oldvincorelen);
1426 oldvincorelen = xd->len - oldvincorelen;
1427 data_addid(&newincore, oldvincorelen);
1432 if (data->attrs[entry])
1433 sat_free(data->attrs[entry]);
1438 sat_free(data->incoredata);
1439 data->incoredata = newincore.buf;
1440 data->incoredatalen = newincore.len;
1441 data->incoredatafree = 0;
1443 sat_free(data->vincore);
1444 data->vincore = newvincore.buf;
1445 data->vincorelen = newvincore.len;
1447 data->attrs = sat_free(data->attrs);
1448 data->attrdata = sat_free(data->attrdata);
1449 data->attriddata = sat_free(data->attriddata);
1450 data->attrdatalen = 0;
1451 data->attriddatalen = 0;
1455 repodata_str2dir(Repodata *data, const char *dir, int create)
1461 while (*dir == '/' && dir[1] == '/')
1463 if (*dir == '/' && !dir[1])
1467 dire = strchrnul(dir, '/');
1468 if (data->localpool)
1469 id = stringpool_strn2id(&data->spool, dir, dire - dir, create);
1471 id = strn2id(data->repo->pool, dir, dire - dir, create);
1474 parent = dirpool_add_dir(&data->dirpool, parent, id, create);
1487 repodata_compress_page(unsigned char *page, unsigned int len, unsigned char *cpage, unsigned int max)
1489 return compress_buf(page, len, cpage, max);
1492 #define SOLV_ERROR_EOF 3
1494 static inline unsigned int
1500 for (i = 0; i < 4; i++)
1510 #define SOLV_ERROR_EOF 3
1511 #define SOLV_ERROR_CORRUPT 6
1513 /* Try to either setup on-demand paging (using FP as backing
1514 file), or in case that doesn't work (FP not seekable) slurps in
1515 all pages and deactivates paging. */
1517 repodata_read_or_setup_pages(Repodata *data, unsigned int pagesz, unsigned int blobsz)
1519 FILE *fp = data->fp;
1520 unsigned int npages;
1522 unsigned int can_seek;
1524 unsigned char buf[BLOB_PAGESIZE];
1526 if (pagesz != BLOB_PAGESIZE)
1528 /* We could handle this by slurping in everything. */
1529 data->error = SOLV_ERROR_CORRUPT;
1533 if ((cur_file_ofs = ftell(fp)) < 0)
1537 data->pagefd = dup(fileno(fp));
1538 if (data->pagefd == -1)
1542 fprintf (stderr, "can %sseek\n", can_seek ? "" : "NOT ");
1544 npages = (blobsz + BLOB_PAGESIZE - 1) / BLOB_PAGESIZE;
1546 data->num_pages = npages;
1547 data->pages = sat_malloc2(npages, sizeof(data->pages[0]));
1549 /* If we can't seek on our input we have to slurp in everything. */
1551 data->blob_store = sat_malloc(npages * BLOB_PAGESIZE);
1552 for (i = 0; i < npages; i++)
1554 unsigned int in_len = read_u32(fp);
1555 unsigned int compressed = in_len & 1;
1556 Attrblobpage *p = data->pages + i;
1559 fprintf (stderr, "page %d: len %d (%scompressed)\n",
1560 i, in_len, compressed ? "" : "not ");
1566 p->file_offset = cur_file_ofs;
1567 p->file_size = in_len * 2 + compressed;
1568 if (fseek(fp, in_len, SEEK_CUR) < 0)
1571 fprintf (stderr, "can't seek after we thought we can\n");
1572 /* We can't fall back to non-seeking behaviour as we already
1573 read over some data pages without storing them away. */
1574 data->error = SOLV_ERROR_EOF;
1575 close(data->pagefd);
1579 cur_file_ofs += in_len;
1583 unsigned int out_len;
1584 void *dest = data->blob_store + i * BLOB_PAGESIZE;
1585 p->mapped_at = i * BLOB_PAGESIZE;
1588 /* We can't seek, so suck everything in. */
1589 if (fread(compressed ? buf : dest, in_len, 1, fp) != 1)
1592 data->error = SOLV_ERROR_EOF;
1597 out_len = unchecked_decompress_buf(buf, in_len, dest, BLOB_PAGESIZE);
1598 if (out_len != BLOB_PAGESIZE && i < npages - 1)
1600 data->error = SOLV_ERROR_CORRUPT;
1609 vim:cinoptions={.5s,g0,p5,t0,(0,^-0.5s,n-0.5s:tw=78:cindent:sw=4: