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 for (keyp = data->attrs[src]; *keyp; keyp += 2)
1153 repodata_insert_keyid(data, dest, keyp[0], keyp[1], 0);
1156 /*********************************/
1158 /* unify with repo_write! */
1160 #define EXTDATA_BLOCK 1023
1161 #define SCHEMATA_BLOCK 31
1162 #define SCHEMATADATA_BLOCK 255
1170 data_addid(struct extdata *xd, Id x)
1173 xd->buf = sat_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
1174 dp = xd->buf + xd->len;
1179 *dp++ = (x >> 28) | 128;
1181 *dp++ = (x >> 21) | 128;
1182 *dp++ = (x >> 14) | 128;
1185 *dp++ = (x >> 7) | 128;
1187 xd->len = dp - xd->buf;
1191 data_addideof(struct extdata *xd, Id x, int eof)
1194 x = (x & 63) | ((x & ~63) << 1);
1195 data_addid(xd, (eof ? x: x | 64));
1199 data_addblob(struct extdata *xd, unsigned char *blob, int len)
1201 xd->buf = sat_extend(xd->buf, xd->len, len, 1, EXTDATA_BLOCK);
1202 memcpy(xd->buf + xd->len, blob, len);
1206 /*********************************/
1209 addschema_prepare(Repodata *data, Id *schematacache)
1214 memset(schematacache, 0, 256 * sizeof(Id));
1215 for (i = 0; i < data->nschemata; i++)
1217 for (sp = data->schemadata + data->schemata[i], h = 0; *sp; len++)
1220 schematacache[h] = i + 1;
1222 data->schemadata = sat_extend_resize(data->schemadata, data->schemadatalen, sizeof(Id), SCHEMATADATA_BLOCK);
1223 data->schemata = sat_extend_resize(data->schemata, data->nschemata, sizeof(Id), SCHEMATA_BLOCK);
1227 addschema(Repodata *data, Id *schema, Id *schematacache)
1232 for (sp = schema, len = 0, h = 0; *sp; len++)
1237 cid = schematacache[h];
1241 if (!memcmp(data->schemadata + data->schemata[cid], schema, len * sizeof(Id)))
1243 /* cache conflict */
1244 for (cid = 0; cid < data->nschemata; cid++)
1245 if (!memcmp(data->schemadata + data->schemata[cid], schema, len * sizeof(Id)))
1248 /* a new one. make room. */
1249 data->schemadata = sat_extend(data->schemadata, data->schemadatalen, len, sizeof(Id), SCHEMATADATA_BLOCK);
1250 data->schemata = sat_extend(data->schemata, data->nschemata, 1, sizeof(Id), SCHEMATA_BLOCK);
1252 memcpy(data->schemadata + data->schemadatalen, schema, len * sizeof(Id));
1253 data->schemata[data->nschemata] = data->schemadatalen;
1254 data->schemadatalen += len;
1255 schematacache[h] = data->nschemata + 1;
1257 fprintf(stderr, "addschema: new schema\n");
1259 return data->nschemata++;
1264 repodata_internalize(Repodata *data)
1267 Id id, entry, nentry, *ida;
1268 Id schematacache[256];
1269 Id schemaid, *schema, *sp, oldschema, *keyp, *seen;
1270 unsigned char *dp, *ndp;
1271 int newschema, oldcount;
1272 struct extdata newincore;
1273 struct extdata newvincore;
1278 newvincore.buf = data->vincore;
1279 newvincore.len = data->vincorelen;
1281 schema = sat_malloc2(data->nkeys, sizeof(Id));
1282 seen = sat_malloc2(data->nkeys, sizeof(Id));
1284 /* Merge the data already existing (in data->schemata, ->incoredata and
1285 friends) with the new attributes in data->attrs[]. */
1286 nentry = data->end - data->start;
1287 addschema_prepare(data, schematacache);
1288 memset(&newincore, 0, sizeof(newincore));
1289 for (entry = 0; entry < nentry; entry++)
1291 memset(seen, 0, data->nkeys * sizeof(Id));
1293 dp = data->incoredata + data->incoreoffset[entry];
1294 if (data->incoredata)
1295 dp = data_read_id(dp, &oldschema);
1299 fprintf(stderr, "oldschema %d\n", oldschema);
1300 fprintf(stderr, "schemata %d\n", data->schemata[oldschema]);
1301 fprintf(stderr, "schemadata %p\n", data->schemadata);
1303 /* seen: -1: old data 0: skipped >0: id + 1 */
1306 for (keyp = data->schemadata + data->schemata[oldschema]; *keyp; keyp++)
1310 fprintf(stderr, "Inconsistent old data (key occured twice).\n");
1317 if (data->attrs[entry])
1318 for (keyp = data->attrs[entry]; *keyp; keyp += 2)
1325 seen[*keyp] = keyp[1] + 1;
1329 /* Ideally we'd like to sort the new schema here, to ensure
1330 schema equality independend of the ordering. We can't do that
1331 yet. For once see below (old ids need to come before new ids).
1332 An additional difficulty is that we also need to move
1333 the values with the keys. */
1334 schemaid = addschema(data, schema, schematacache);
1336 schemaid = oldschema;
1339 /* Now create data blob. We walk through the (possibly new) schema
1340 and either copy over old data, or insert the new. */
1341 /* XXX Here we rely on the fact that the (new) schema has the form
1342 o1 o2 o3 o4 ... | n1 n2 n3 ...
1343 (oX being the old keyids (possibly overwritten), and nX being
1344 the new keyids). This rules out sorting the keyids in order
1345 to ensure a small schema count. */
1346 data->incoreoffset[entry] = newincore.len;
1347 data_addid(&newincore, schemaid);
1348 for (keyp = data->schemadata + data->schemata[schemaid]; *keyp; keyp++)
1350 key = data->keys + *keyp;
1354 /* Skip the data associated with this old key. */
1355 if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
1357 ndp = data_skip(dp, REPOKEY_TYPE_ID);
1358 ndp = data_skip(ndp, REPOKEY_TYPE_ID);
1360 else if (key->storage == KEY_STORAGE_INCORE)
1361 ndp = data_skip(dp, key->type);
1364 if (seen[*keyp] == -1)
1366 /* If this key was an old one _and_ was not overwritten with
1367 a different value copy over the old value (we skipped it
1370 data_addblob(&newincore, dp, ndp - dp);
1373 else if (seen[*keyp])
1375 /* Otherwise we have a new value. Parse it into the internal
1378 unsigned int oldvincorelen = 0;
1381 if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
1384 oldvincorelen = xd->len;
1386 id = seen[*keyp] - 1;
1389 case REPOKEY_TYPE_VOID:
1390 case REPOKEY_TYPE_CONSTANT:
1391 case REPOKEY_TYPE_CONSTANTID:
1393 case REPOKEY_TYPE_STR:
1394 data_addblob(xd, data->attrdata + id, strlen((char *)(data->attrdata + id)) + 1);
1396 case REPOKEY_TYPE_ID:
1397 case REPOKEY_TYPE_NUM:
1398 case REPOKEY_TYPE_DIR:
1401 case REPOKEY_TYPE_DIRNUMNUMARRAY:
1402 for (ida = data->attriddata + id; *ida; ida += 3)
1404 data_addid(xd, ida[0]);
1405 data_addid(xd, ida[1]);
1406 data_addideof(xd, ida[2], ida[3] ? 0 : 1);
1409 case REPOKEY_TYPE_DIRSTRARRAY:
1410 for (ida = data->attriddata + id; *ida; ida += 2)
1412 data_addideof(xd, ida[0], ida[2] ? 0 : 1);
1413 data_addblob(xd, data->attrdata + ida[1], strlen((char *)(data->attrdata + ida[1])) + 1);
1417 fprintf(stderr, "don't know how to handle type %d\n", key->type);
1420 if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
1422 /* put offset/len in incore */
1423 data_addid(&newincore, data->lastverticaloffset + oldvincorelen);
1424 oldvincorelen = xd->len - oldvincorelen;
1425 data_addid(&newincore, oldvincorelen);
1430 if (data->attrs[entry])
1431 sat_free(data->attrs[entry]);
1436 sat_free(data->incoredata);
1437 data->incoredata = newincore.buf;
1438 data->incoredatalen = newincore.len;
1439 data->incoredatafree = 0;
1441 sat_free(data->vincore);
1442 data->vincore = newvincore.buf;
1443 data->vincorelen = newvincore.len;
1445 data->attrs = sat_free(data->attrs);
1446 data->attrdata = sat_free(data->attrdata);
1447 data->attriddata = sat_free(data->attriddata);
1448 data->attrdatalen = 0;
1449 data->attriddatalen = 0;
1453 repodata_str2dir(Repodata *data, const char *dir, int create)
1459 while (*dir == '/' && dir[1] == '/')
1461 if (*dir == '/' && !dir[1])
1465 dire = strchrnul(dir, '/');
1466 if (data->localpool)
1467 id = stringpool_strn2id(&data->spool, dir, dire - dir, create);
1469 id = strn2id(data->repo->pool, dir, dire - dir, create);
1472 parent = dirpool_add_dir(&data->dirpool, parent, id, create);
1485 repodata_compress_page(unsigned char *page, unsigned int len, unsigned char *cpage, unsigned int max)
1487 return compress_buf(page, len, cpage, max);
1490 #define SOLV_ERROR_EOF 3
1492 static inline unsigned int
1498 for (i = 0; i < 4; i++)
1508 #define SOLV_ERROR_EOF 3
1509 #define SOLV_ERROR_CORRUPT 6
1511 /* Try to either setup on-demand paging (using FP as backing
1512 file), or in case that doesn't work (FP not seekable) slurps in
1513 all pages and deactivates paging. */
1515 repodata_read_or_setup_pages(Repodata *data, unsigned int pagesz, unsigned int blobsz)
1517 FILE *fp = data->fp;
1518 unsigned int npages;
1520 unsigned int can_seek;
1522 unsigned char buf[BLOB_PAGESIZE];
1524 if (pagesz != BLOB_PAGESIZE)
1526 /* We could handle this by slurping in everything. */
1527 data->error = SOLV_ERROR_CORRUPT;
1531 if ((cur_file_ofs = ftell(fp)) < 0)
1535 data->pagefd = dup(fileno(fp));
1536 if (data->pagefd == -1)
1540 fprintf (stderr, "can %sseek\n", can_seek ? "" : "NOT ");
1542 npages = (blobsz + BLOB_PAGESIZE - 1) / BLOB_PAGESIZE;
1544 data->num_pages = npages;
1545 data->pages = sat_malloc2(npages, sizeof(data->pages[0]));
1547 /* If we can't seek on our input we have to slurp in everything. */
1549 data->blob_store = sat_malloc(npages * BLOB_PAGESIZE);
1550 for (i = 0; i < npages; i++)
1552 unsigned int in_len = read_u32(fp);
1553 unsigned int compressed = in_len & 1;
1554 Attrblobpage *p = data->pages + i;
1557 fprintf (stderr, "page %d: len %d (%scompressed)\n",
1558 i, in_len, compressed ? "" : "not ");
1564 p->file_offset = cur_file_ofs;
1565 p->file_size = in_len * 2 + compressed;
1566 if (fseek(fp, in_len, SEEK_CUR) < 0)
1569 fprintf (stderr, "can't seek after we thought we can\n");
1570 /* We can't fall back to non-seeking behaviour as we already
1571 read over some data pages without storing them away. */
1572 data->error = SOLV_ERROR_EOF;
1573 close(data->pagefd);
1577 cur_file_ofs += in_len;
1581 unsigned int out_len;
1582 void *dest = data->blob_store + i * BLOB_PAGESIZE;
1583 p->mapped_at = i * BLOB_PAGESIZE;
1586 /* We can't seek, so suck everything in. */
1587 if (fread(compressed ? buf : dest, in_len, 1, fp) != 1)
1590 data->error = SOLV_ERROR_EOF;
1595 out_len = unchecked_decompress_buf(buf, in_len, dest, BLOB_PAGESIZE);
1596 if (out_len != BLOB_PAGESIZE && i < npages - 1)
1598 data->error = SOLV_ERROR_CORRUPT;
1607 vim:cinoptions={.5s,g0,p5,t0,(0,^-0.5s,n-0.5s:tw=78:cindent:sw=4: