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 /* Ensure that we can map the numbers of pages we need at all. */
119 if (pend - pstart + 1 > data->ncanmap)
121 unsigned int oldcan = data->ncanmap;
122 data->ncanmap = pend - pstart + 1;
123 if (data->ncanmap < 4)
125 data->mapped = sat_realloc2(data->mapped, data->ncanmap, sizeof(data->mapped[0]));
126 memset (data->mapped + oldcan, 0, (data->ncanmap - oldcan) * sizeof (data->mapped[0]));
127 data->blob_store = sat_realloc2(data->blob_store, data->ncanmap, BLOB_PAGESIZE);
129 fprintf (stderr, "PAGE: can map %d pages\n", data->ncanmap);
133 /* Now search for "cheap" space in our store. Space is cheap if it's either
134 free (very cheap) or contains pages we search for anyway. */
136 /* Setup cost array. */
137 unsigned int cost[data->ncanmap];
138 for (i = 0; i < data->ncanmap; i++)
140 unsigned int pnum = data->mapped[i];
146 Attrblobpage *p = data->pages + pnum;
147 assert (p->mapped_at != -1);
148 if (pnum >= pstart && pnum <= pend)
155 /* And search for cheapest space. */
156 unsigned int best_cost = -1;
157 unsigned int best = 0;
158 unsigned int same_cost = 0;
159 for (i = 0; i + pend - pstart < data->ncanmap; i++)
161 unsigned int c = cost[i];
163 for (j = 0; j < pend - pstart + 1; j++)
166 best_cost = c, best = i;
167 else if (c == best_cost)
169 /* A null cost won't become better. */
173 /* If all places have the same cost we would thrash on slot 0. Avoid
174 this by doing a round-robin strategy in this case. */
175 if (same_cost == data->ncanmap - pend + pstart - 1)
176 best = data->rr_counter++ % (data->ncanmap - pend + pstart);
178 /* So we want to map our pages from [best] to [best+pend-pstart].
179 Use a very simple strategy, which doesn't make the best use of
180 our resources, but works. Throw away all pages in that range
181 (even ours) then copy around ours (in case they were outside the
182 range) or read them in. */
183 for (i = best; i < best + pend - pstart + 1; i++)
185 unsigned int pnum = data->mapped[i];
187 /* If this page is exactly at the right place already,
188 no need to evict it. */
189 && pnum != pstart + i - best)
191 /* Evict this page. */
193 fprintf (stderr, "PAGE: evict page %d from %d\n", pnum, i);
197 data->pages[pnum].mapped_at = -1;
201 /* Everything is free now. Read in the pages we want. */
202 for (i = pstart; i <= pend; i++)
204 Attrblobpage *p = data->pages + i;
205 unsigned int pnum = i - pstart + best;
206 void *dest = data->blob_store + pnum * BLOB_PAGESIZE;
207 if (p->mapped_at != -1)
209 if (p->mapped_at != pnum * BLOB_PAGESIZE)
212 fprintf (stderr, "PAGECOPY: %d to %d\n", i, pnum);
214 /* Still mapped somewhere else, so just copy it from there. */
215 memcpy (dest, data->blob_store + p->mapped_at, BLOB_PAGESIZE);
216 data->mapped[p->mapped_at / BLOB_PAGESIZE] = 0;
221 unsigned int in_len = p->file_size;
222 unsigned int compressed = in_len & 1;
225 fprintf (stderr, "PAGEIN: %d to %d", i, pnum);
227 if (pread(data->pagefd, compressed ? buf : dest, in_len, p->file_offset) != in_len)
229 perror ("mapping pread");
234 unsigned int out_len;
235 out_len = unchecked_decompress_buf(buf, in_len,
236 dest, BLOB_PAGESIZE);
237 if (out_len != BLOB_PAGESIZE && i < data->num_pages - 1)
239 fprintf(stderr, "can't decompress\n");
243 fprintf (stderr, " (expand %d to %d)", in_len, out_len);
247 fprintf (stderr, "\n");
250 p->mapped_at = pnum * BLOB_PAGESIZE;
251 data->mapped[pnum] = i + 1;
253 return data->blob_store + best * BLOB_PAGESIZE;
256 static unsigned char *
257 make_vertical_available(Repodata *data, Repokey *key, Id off, Id len)
260 if (key->type == REPOKEY_TYPE_VOID)
262 if (off >= data->lastverticaloffset)
264 off -= data->lastverticaloffset;
265 if (off + len > data->vincorelen)
267 return data->vincore + off;
269 if (data->pagefd == -1)
271 if (off + len > key->size)
273 /* we now have the offset, go into vertical */
274 off += data->verticaloffset[key - data->keys];
275 dp = load_page_range(data, off / BLOB_PAGESIZE, (off + len - 1) / BLOB_PAGESIZE);
277 dp += off % BLOB_PAGESIZE;
281 static inline unsigned char *
282 get_data(Repodata *data, Repokey *key, unsigned char **dpp)
284 unsigned char *dp = *dpp;
288 if (key->storage == KEY_STORAGE_INCORE)
290 /* hmm, this is a bit expensive */
291 *dpp = data_skip(dp, key->type);
294 else if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
297 dp = data_read_id(dp, &off);
298 dp = data_read_id(dp, &len);
300 return make_vertical_available(data, key, off, len);
306 maybe_load_repodata(Repodata *data, Id *keyid)
308 if (data->state == REPODATA_STUB)
310 if (data->loadcallback)
314 /* key order may change when loading */
316 Id name = data->keys[*keyid].name;
317 Id type = data->keys[*keyid].type;
318 data->loadcallback(data);
319 if (data->state == REPODATA_AVAILABLE)
321 for (i = 1; i < data->nkeys; i++)
322 if (data->keys[i].name == name && data->keys[i].type == type)
331 data->loadcallback(data);
334 data->state = REPODATA_ERROR;
336 if (data->state == REPODATA_AVAILABLE)
338 data->state = REPODATA_ERROR;
343 repodata_lookup_str(Repodata *data, Id entry, Id keyid)
350 if (!maybe_load_repodata(data, &keyid))
353 dp = data->incoredata + data->incoreoffset[entry];
354 dp = data_read_id(dp, &schema);
355 /* make sure the schema of this solvable contains the key */
356 for (keyp = data->schemadata + data->schemata[schema]; *keyp != keyid; keyp++)
359 dp = forward_to_key(data, keyid, schema, dp);
360 key = data->keys + keyid;
361 dp = get_data(data, key, &dp);
364 if (key->type == REPOKEY_TYPE_STR)
365 return (const char *)dp;
366 if (key->type == REPOKEY_TYPE_CONSTANTID)
367 return id2str(data->repo->pool, key->size);
368 if (key->type == REPOKEY_TYPE_ID)
369 dp = data_read_id(dp, &id);
373 return data->spool.stringspace + data->spool.strings[id];
374 return id2str(data->repo->pool, id);
378 repodata_lookup_num(Repodata *data, Id entry, Id keyid, unsigned int *value)
388 if (!maybe_load_repodata(data, &keyid))
391 dp = data->incoredata + data->incoreoffset[entry];
392 dp = data_read_id(dp, &schema);
393 /* make sure the schema of this solvable contains the key */
394 for (keyp = data->schemadata + data->schemata[schema]; *keyp != keyid; keyp++)
397 dp = forward_to_key(data, keyid, schema, dp);
398 key = data->keys + keyid;
399 dp = get_data(data, key, &dp);
402 if (key->type == REPOKEY_TYPE_NUM
403 || key->type == REPOKEY_TYPE_U32
404 || key->type == REPOKEY_TYPE_CONSTANT)
406 dp = data_fetch(dp, &kv, key);
414 repodata_lookup_void(Repodata *data, Id entry, Id keyid)
419 if (!maybe_load_repodata(data, &keyid))
421 dp = data->incoredata + data->incoreoffset[entry];
422 dp = data_read_id(dp, &schema);
423 for (keyp = data->schemadata + data->schemata[schema]; *keyp != keyid; keyp++)
430 repodata_search(Repodata *data, Id entry, Id keyname, int (*callback)(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv), void *cbdata)
434 Id k, keyid, *kp, *keyp;
435 unsigned char *dp, *ddp;
440 if (!maybe_load_repodata(data, 0))
443 dp = data->incoredata + data->incoreoffset[entry];
444 dp = data_read_id(dp, &schema);
445 keyp = data->schemadata + data->schemata[schema];
448 /* search in a specific key */
449 for (kp = keyp; (k = *kp++) != 0; )
450 if (data->keys[k].name == keyname)
454 dp = forward_to_key(data, k, schema, dp);
460 while ((keyid = *keyp++) != 0)
463 key = data->keys + keyid;
464 ddp = get_data(data, key, &dp);
467 ddp = data_fetch(ddp, &kv, key);
470 stop = callback(cbdata, data->repo->pool->solvables + data->start + entry, data, key, &kv);
472 while (!kv.eof && !stop);
473 if (onekey || stop > SEARCH_NEXT_KEY)
479 dataiterator_newdata(Dataiterator *di)
481 Id keyname = di->keyname;
482 Repodata *data = di->data;
485 if (data->state == REPODATA_STUB)
490 for (j = 1; j < data->nkeys; j++)
491 if (keyname == data->keys[j].name)
493 if (j == data->nkeys)
497 if (data->loadcallback)
498 data->loadcallback(data);
500 data->state = REPODATA_ERROR;
502 if (data->state == REPODATA_ERROR)
506 unsigned char *dp = data->incoredata + data->incoreoffset[di->solvid - data->start];
507 dp = data_read_id(dp, &schema);
508 Id *keyp = data->schemadata + data->schemata[schema];
512 /* search in a specific key */
513 for (kp = keyp; (k = *kp++) != 0; )
514 if (data->keys[k].name == keyname)
518 dp = forward_to_key(data, k, schema, dp);
528 di->key = di->data->keys + keyid;
533 di->dp = get_data(di->data, di->key, &di->nextkeydp);
538 dataiterator_init(Dataiterator *di, Repo *repo, Id p, Id keyname,
539 const char *match, int flags)
545 di->flags |= __SEARCH_ONESOLVABLE;
546 di->data = repo->repodata - 1;
547 if (flags & SEARCH_NO_STORAGE_SOLVABLE)
554 di->solvid = repo->start - 1;
555 di->data = repo->repodata + repo->nrepodata - 1;
559 di->keyname = keyname;
560 static Id zeroid = 0;
567 /* FIXME factor and merge with repo_matchvalue */
569 dataiterator_match(Dataiterator *di, KeyValue *kv)
571 int flags = di->flags;
573 if ((flags & SEARCH_STRINGMASK) != 0)
575 switch (di->key->type)
577 case REPOKEY_TYPE_ID:
578 case REPOKEY_TYPE_IDARRAY:
579 if (di->data && di->data->localpool)
580 kv->str = stringpool_id2str(&di->data->spool, kv->id);
582 kv->str = id2str(di->repo->pool, kv->id);
584 case REPOKEY_TYPE_STR:
589 switch ((flags & SEARCH_STRINGMASK))
591 case SEARCH_SUBSTRING:
592 if (flags & SEARCH_NOCASE)
594 if (!strcasestr(kv->str, di->match))
599 if (!strstr(kv->str, di->match))
604 if (flags & SEARCH_NOCASE)
606 if (strcasecmp(di->match, kv->str))
611 if (strcmp(di->match, kv->str))
616 if (fnmatch(di->match, kv->str, (flags & SEARCH_NOCASE) ? FNM_CASEFOLD : 0))
621 if (regexec(&di->regexp, kv->str, 0, NULL, 0))
631 static Repokey solvablekeys[RPM_RPMDBID - SOLVABLE_NAME + 1] = {
632 { SOLVABLE_NAME, REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
633 { SOLVABLE_ARCH, REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
634 { SOLVABLE_EVR, REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
635 { SOLVABLE_VENDOR, REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
636 { SOLVABLE_PROVIDES, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
637 { SOLVABLE_OBSOLETES, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
638 { SOLVABLE_CONFLICTS, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
639 { SOLVABLE_REQUIRES, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
640 { SOLVABLE_RECOMMENDS, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
641 { SOLVABLE_SUGGESTS, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
642 { SOLVABLE_SUPPLEMENTS, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
643 { SOLVABLE_ENHANCES, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
644 { SOLVABLE_FRESHENS, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
645 { RPM_RPMDBID, REPOKEY_TYPE_U32, 0, KEY_STORAGE_SOLVABLE },
649 dataiterator_step(Dataiterator *di)
663 di->kv.eof = idp[1] ? 0 : 1;
669 Solvable *s = di->repo->pool->solvables + di->solvid;
670 int state = di->state;
671 di->key = solvablekeys + state - 1;
673 di->state = RPM_RPMDBID;
680 state = di->keyname - 1;
699 case SOLVABLE_VENDOR:
702 di->kv.id = s->vendor;
704 case SOLVABLE_PROVIDES:
705 di->idp = s->provides
706 ? di->repo->idarraydata + s->provides : 0;
708 case SOLVABLE_OBSOLETES:
709 di->idp = s->obsoletes
710 ? di->repo->idarraydata + s->obsoletes : 0;
712 case SOLVABLE_CONFLICTS:
713 di->idp = s->conflicts
714 ? di->repo->idarraydata + s->conflicts : 0;
716 case SOLVABLE_REQUIRES:
717 di->idp = s->requires
718 ? di->repo->idarraydata + s->requires : 0;
720 case SOLVABLE_RECOMMENDS:
721 di->idp = s->recommends
722 ? di->repo->idarraydata + s->recommends : 0;
724 case SOLVABLE_SUPPLEMENTS:
725 di->idp = s->supplements
726 ? di->repo->idarraydata + s->supplements : 0;
728 case SOLVABLE_SUGGESTS:
729 di->idp = s->suggests
730 ? di->repo->idarraydata + s->suggests : 0;
732 case SOLVABLE_ENHANCES:
733 di->idp = s->enhances
734 ? di->repo->idarraydata + s->enhances : 0;
736 case SOLVABLE_FRESHENS:
737 di->idp = s->freshens
738 ? di->repo->idarraydata + s->freshens : 0;
741 if (!di->repo->rpmdbid)
743 di->kv.num = di->repo->rpmdbid[di->solvid - di->repo->start];
746 di->data = di->repo->repodata - 1;
757 di->dp = data_fetch(di->dp, &di->kv, di->key);
762 if (di->keyname || !(keyid = *di->keyp++))
766 Repo *repo = di->repo;
767 Repodata *data = ++di->data;
768 if (data >= repo->repodata + repo->nrepodata)
770 if (di->flags & __SEARCH_ONESOLVABLE)
772 while (++di->solvid < repo->end)
773 if (repo->pool->solvables[di->solvid].repo == repo)
775 if (di->solvid >= repo->end)
777 di->data = repo->repodata - 1;
778 if (di->flags & SEARCH_NO_STORAGE_SOLVABLE)
780 static Id zeroid = 0;
785 if (di->solvid >= data->start && di->solvid < data->end)
787 dataiterator_newdata(di);
795 di->key = di->data->keys + keyid;
796 di->dp = get_data(di->data, di->key, &di->nextkeydp);
798 di->dp = data_fetch(di->dp, &di->kv, di->key);
803 || dataiterator_match(di, &di->kv))
810 repodata_init(Repodata *data, Repo *repo, int localpool)
812 memset(data, 0, sizeof (*data));
814 data->localpool = localpool;
816 stringpool_init_empty(&data->spool);
817 data->keys = sat_calloc(1, sizeof(Repokey));
819 data->schemata = sat_calloc(1, sizeof(Id));
820 data->schemadata = sat_calloc(1, sizeof(Id));
822 data->schemadatalen = 1;
823 data->start = repo->start;
824 data->end = repo->end;
825 data->incoreoffset = sat_extend_resize(0, data->end - data->start, sizeof(Id), REPODATA_BLOCK);
829 /* extend repodata so that it includes solvables p */
831 repodata_extend(Repodata *data, Id p)
833 if (data->start == data->end)
834 data->start = data->end = p;
837 int old = data->end - data->start;
838 int new = p - data->end + 1;
841 data->attrs = sat_extend(data->attrs, old, new, sizeof(Id *), REPODATA_BLOCK);
842 memset(data->attrs + old, 0, new * sizeof(Id *));
844 data->incoreoffset = sat_extend(data->incoreoffset, old, new, sizeof(Id), REPODATA_BLOCK);
845 memset(data->incoreoffset + old, 0, new * sizeof(Id));
850 int old = data->end - data->start;
851 int new = data->start - p;
854 data->attrs = sat_extend_resize(data->attrs, old + new, sizeof(Id *), REPODATA_BLOCK);
855 memmove(data->attrs + new, data->attrs, old * sizeof(Id *));
856 memset(data->attrs, 0, new * sizeof(Id *));
858 data->incoreoffset = sat_extend_resize(data->incoreoffset, old + new, sizeof(Id), REPODATA_BLOCK);
859 memmove(data->incoreoffset + new, data->incoreoffset, old * sizeof(Id));
860 memset(data->incoreoffset, 0, new * sizeof(Id));
866 repodata_extend_block(Repodata *data, Id start, Id num)
870 if (!data->incoreoffset)
872 data->incoreoffset = sat_extend_resize(data->incoreoffset, num, sizeof(Id), REPODATA_BLOCK);
873 memset(data->incoreoffset, 0, num * sizeof(Id));
875 data->end = start + num;
878 repodata_extend(data, start);
880 repodata_extend(data, start + num - 1);
883 #define REPODATA_ATTRS_BLOCK 63
884 #define REPODATA_ATTRDATA_BLOCK 1023
885 #define REPODATA_ATTRIDDATA_BLOCK 63
888 repodata_insert_keyid(Repodata *data, Id entry, Id keyid, Id val, int overwrite)
894 data->attrs = sat_extend_resize(0, data->end - data->start, sizeof(Id *), REPODATA_BLOCK);
895 memset(data->attrs, 0, (data->end - data->start) * sizeof(Id *));
898 if (data->attrs[entry])
900 for (pp = data->attrs[entry]; *pp; pp += 2)
901 /* Determine equality based on the name only, allows us to change
902 type (when overwrite is set), and makes TYPE_CONSTANT work. */
903 if (data->keys[*pp].name == data->keys[keyid].name)
914 i = pp - data->attrs[entry];
916 data->attrs[entry] = sat_extend(data->attrs[entry], i, 3, sizeof(Id), REPODATA_ATTRS_BLOCK);
917 pp = data->attrs[entry] + i;
924 repodata_set(Repodata *data, Id entry, Repokey *key, Id val)
928 /* find key in keys */
929 for (keyid = 1; keyid < data->nkeys; keyid++)
930 if (data->keys[keyid].name == key->name && data->keys[keyid].type == key->type)
932 if ((key->type == REPOKEY_TYPE_CONSTANT || key->type == REPOKEY_TYPE_CONSTANTID) && key->size != data->keys[keyid].size)
936 if (keyid == data->nkeys)
938 /* allocate new key */
939 data->keys = sat_realloc2(data->keys, data->nkeys + 1, sizeof(Repokey));
940 data->keys[data->nkeys++] = *key;
941 if (data->verticaloffset)
943 data->verticaloffset = sat_realloc2(data->verticaloffset, data->nkeys, sizeof(Id));
944 data->verticaloffset[data->nkeys - 1] = 0;
947 repodata_insert_keyid(data, entry, keyid, val, 1);
951 repodata_set_id(Repodata *data, Id entry, Id keyname, Id id)
955 key.type = REPOKEY_TYPE_ID;
957 key.storage = KEY_STORAGE_INCORE;
958 repodata_set(data, entry, &key, id);
962 repodata_set_num(Repodata *data, Id entry, Id keyname, Id num)
966 key.type = REPOKEY_TYPE_NUM;
968 key.storage = KEY_STORAGE_INCORE;
969 repodata_set(data, entry, &key, num);
973 repodata_set_poolstr(Repodata *data, Id entry, Id keyname, const char *str)
978 id = stringpool_str2id(&data->spool, str, 1);
980 id = str2id(data->repo->pool, str, 1);
982 key.type = REPOKEY_TYPE_ID;
984 key.storage = KEY_STORAGE_INCORE;
985 repodata_set(data, entry, &key, id);
989 repodata_set_constant(Repodata *data, Id entry, Id keyname, Id constant)
993 key.type = REPOKEY_TYPE_CONSTANT;
995 key.storage = KEY_STORAGE_INCORE;
996 repodata_set(data, entry, &key, 0);
1000 repodata_set_constantid(Repodata *data, Id entry, Id keyname, Id id)
1004 key.type = REPOKEY_TYPE_CONSTANTID;
1006 key.storage = KEY_STORAGE_INCORE;
1007 repodata_set(data, entry, &key, 0);
1011 repodata_set_void(Repodata *data, Id entry, Id keyname)
1015 key.type = REPOKEY_TYPE_VOID;
1017 key.storage = KEY_STORAGE_INCORE;
1018 repodata_set(data, entry, &key, 0);
1022 repodata_set_str(Repodata *data, Id entry, Id keyname, const char *str)
1027 l = strlen(str) + 1;
1029 key.type = REPOKEY_TYPE_STR;
1031 key.storage = KEY_STORAGE_INCORE;
1032 data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
1033 memcpy(data->attrdata + data->attrdatalen, str, l);
1034 repodata_set(data, entry, &key, data->attrdatalen);
1035 data->attrdatalen += l;
1039 repodata_add_dirnumnum(Repodata *data, Id entry, Id keyname, Id dir, Id num, Id num2)
1045 fprintf(stderr, "repodata_add_dirnumnum %d %d %d %d (%d)\n", entry, dir, num, num2, data->attriddatalen);
1047 if (data->attrs && data->attrs[entry])
1049 for (pp = data->attrs[entry]; *pp; pp += 2)
1050 if (data->keys[*pp].name == keyname && data->keys[*pp].type == REPOKEY_TYPE_DIRNUMNUMARRAY)
1055 for (ida = data->attriddata + pp[1]; *ida; ida += 3)
1057 if (ida + 1 == data->attriddata + data->attriddatalen)
1059 /* this was the last entry, just append it */
1060 data->attriddata = sat_extend(data->attriddata, data->attriddatalen, 3, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1061 data->attriddatalen--; /* overwrite terminating 0 */
1065 /* too bad. move to back. */
1066 data->attriddata = sat_extend(data->attriddata, data->attriddatalen, oldsize + 4, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1067 memcpy(data->attriddata + data->attriddatalen, data->attriddata + pp[1], oldsize * sizeof(Id));
1068 pp[1] = data->attriddatalen;
1069 data->attriddatalen += oldsize;
1071 data->attriddata[data->attriddatalen++] = dir;
1072 data->attriddata[data->attriddatalen++] = num;
1073 data->attriddata[data->attriddatalen++] = num2;
1074 data->attriddata[data->attriddatalen++] = 0;
1079 key.type = REPOKEY_TYPE_DIRNUMNUMARRAY;
1081 key.storage = KEY_STORAGE_INCORE;
1082 data->attriddata = sat_extend(data->attriddata, data->attriddatalen, 4, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1083 repodata_set(data, entry, &key, data->attriddatalen);
1084 data->attriddata[data->attriddatalen++] = dir;
1085 data->attriddata[data->attriddatalen++] = num;
1086 data->attriddata[data->attriddatalen++] = num2;
1087 data->attriddata[data->attriddatalen++] = 0;
1091 repodata_add_dirstr(Repodata *data, Id entry, Id keyname, Id dir, const char *str)
1093 Id *ida, *pp, stroff;
1097 l = strlen(str) + 1;
1098 data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
1099 memcpy(data->attrdata + data->attrdatalen, str, l);
1100 stroff = data->attrdatalen;
1101 data->attrdatalen += l;
1104 fprintf(stderr, "repodata_add_dirstr %d %d %s (%d)\n", entry, dir, str, data->attriddatalen);
1106 if (data->attrs && data->attrs[entry])
1108 for (pp = data->attrs[entry]; *pp; pp += 2)
1109 if (data->keys[*pp].name == keyname && data->keys[*pp].type == REPOKEY_TYPE_DIRSTRARRAY)
1114 for (ida = data->attriddata + pp[1]; *ida; ida += 2)
1116 if (ida + 1 == data->attriddata + data->attriddatalen)
1118 /* this was the last entry, just append it */
1119 data->attriddata = sat_extend(data->attriddata, data->attriddatalen, 2, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1120 data->attriddatalen--; /* overwrite terminating 0 */
1124 /* too bad. move to back. */
1125 data->attriddata = sat_extend(data->attriddata, data->attriddatalen, oldsize + 3, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1126 memcpy(data->attriddata + data->attriddatalen, data->attriddata + pp[1], oldsize * sizeof(Id));
1127 pp[1] = data->attriddatalen;
1128 data->attriddatalen += oldsize;
1130 data->attriddata[data->attriddatalen++] = dir;
1131 data->attriddata[data->attriddatalen++] = stroff;
1132 data->attriddata[data->attriddatalen++] = 0;
1137 key.type = REPOKEY_TYPE_DIRSTRARRAY;
1139 key.storage = KEY_STORAGE_INCORE;
1140 data->attriddata = sat_extend(data->attriddata, data->attriddatalen, 3, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1141 repodata_set(data, entry, &key, data->attriddatalen);
1142 data->attriddata[data->attriddatalen++] = dir;
1143 data->attriddata[data->attriddatalen++] = stroff;
1144 data->attriddata[data->attriddatalen++] = 0;
1148 repodata_merge_attrs (Repodata *data, Id dest, Id src)
1151 for (keyp = data->attrs[src]; *keyp; keyp += 2)
1152 repodata_insert_keyid(data, dest, keyp[0], keyp[1], 0);
1155 /*********************************/
1157 /* unify with repo_write! */
1159 #define EXTDATA_BLOCK 1023
1160 #define SCHEMATA_BLOCK 31
1161 #define SCHEMATADATA_BLOCK 255
1169 data_addid(struct extdata *xd, Id x)
1172 xd->buf = sat_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
1173 dp = xd->buf + xd->len;
1178 *dp++ = (x >> 28) | 128;
1180 *dp++ = (x >> 21) | 128;
1181 *dp++ = (x >> 14) | 128;
1184 *dp++ = (x >> 7) | 128;
1186 xd->len = dp - xd->buf;
1190 data_addideof(struct extdata *xd, Id x, int eof)
1193 x = (x & 63) | ((x & ~63) << 1);
1194 data_addid(xd, (eof ? x: x | 64));
1198 data_addblob(struct extdata *xd, unsigned char *blob, int len)
1200 xd->buf = sat_extend(xd->buf, xd->len, len, 1, EXTDATA_BLOCK);
1201 memcpy(xd->buf + xd->len, blob, len);
1205 /*********************************/
1208 addschema_prepare(Repodata *data, Id *schematacache)
1213 memset(schematacache, 0, 256 * sizeof(Id));
1214 for (i = 0; i < data->nschemata; i++)
1216 for (sp = data->schemadata + data->schemata[i], h = 0; *sp; len++)
1219 schematacache[h] = i + 1;
1221 data->schemadata = sat_extend_resize(data->schemadata, data->schemadatalen, sizeof(Id), SCHEMATADATA_BLOCK);
1222 data->schemata = sat_extend_resize(data->schemata, data->nschemata, sizeof(Id), SCHEMATA_BLOCK);
1226 addschema(Repodata *data, Id *schema, Id *schematacache)
1231 for (sp = schema, len = 0, h = 0; *sp; len++)
1236 cid = schematacache[h];
1240 if (!memcmp(data->schemadata + data->schemata[cid], schema, len * sizeof(Id)))
1242 /* cache conflict */
1243 for (cid = 0; cid < data->nschemata; cid++)
1244 if (!memcmp(data->schemadata + data->schemata[cid], schema, len * sizeof(Id)))
1247 /* a new one. make room. */
1248 data->schemadata = sat_extend(data->schemadata, data->schemadatalen, len, sizeof(Id), SCHEMATADATA_BLOCK);
1249 data->schemata = sat_extend(data->schemata, data->nschemata, 1, sizeof(Id), SCHEMATA_BLOCK);
1251 memcpy(data->schemadata + data->schemadatalen, schema, len * sizeof(Id));
1252 data->schemata[data->nschemata] = data->schemadatalen;
1253 data->schemadatalen += len;
1254 schematacache[h] = data->nschemata + 1;
1256 fprintf(stderr, "addschema: new schema\n");
1258 return data->nschemata++;
1263 repodata_internalize(Repodata *data)
1266 Id id, entry, nentry, *ida;
1267 Id schematacache[256];
1268 Id schemaid, *schema, *sp, oldschema, *keyp, *seen;
1269 unsigned char *dp, *ndp;
1270 int newschema, oldcount;
1271 struct extdata newincore;
1272 struct extdata newvincore;
1277 newvincore.buf = data->vincore;
1278 newvincore.len = data->vincorelen;
1280 schema = sat_malloc2(data->nkeys, sizeof(Id));
1281 seen = sat_malloc2(data->nkeys, sizeof(Id));
1283 /* Merge the data already existing (in data->schemata, ->incoredata and
1284 friends) with the new attributes in data->attrs[]. */
1285 nentry = data->end - data->start;
1286 addschema_prepare(data, schematacache);
1287 memset(&newincore, 0, sizeof(newincore));
1288 for (entry = 0; entry < nentry; entry++)
1290 memset(seen, 0, data->nkeys * sizeof(Id));
1292 dp = data->incoredata + data->incoreoffset[entry];
1293 if (data->incoredata)
1294 dp = data_read_id(dp, &oldschema);
1298 fprintf(stderr, "oldschema %d\n", oldschema);
1299 fprintf(stderr, "schemata %d\n", data->schemata[oldschema]);
1300 fprintf(stderr, "schemadata %p\n", data->schemadata);
1302 /* seen: -1: old data 0: skipped >0: id + 1 */
1305 for (keyp = data->schemadata + data->schemata[oldschema]; *keyp; keyp++)
1309 fprintf(stderr, "Inconsistent old data (key occured twice).\n");
1316 if (data->attrs[entry])
1317 for (keyp = data->attrs[entry]; *keyp; keyp += 2)
1324 seen[*keyp] = keyp[1] + 1;
1328 /* Ideally we'd like to sort the new schema here, to ensure
1329 schema equality independend of the ordering. We can't do that
1330 yet. For once see below (old ids need to come before new ids).
1331 An additional difficulty is that we also need to move
1332 the values with the keys. */
1333 schemaid = addschema(data, schema, schematacache);
1335 schemaid = oldschema;
1338 /* Now create data blob. We walk through the (possibly new) schema
1339 and either copy over old data, or insert the new. */
1340 /* XXX Here we rely on the fact that the (new) schema has the form
1341 o1 o2 o3 o4 ... | n1 n2 n3 ...
1342 (oX being the old keyids (possibly overwritten), and nX being
1343 the new keyids). This rules out sorting the keyids in order
1344 to ensure a small schema count. */
1345 data->incoreoffset[entry] = newincore.len;
1346 data_addid(&newincore, schemaid);
1347 for (keyp = data->schemadata + data->schemata[schemaid]; *keyp; keyp++)
1349 key = data->keys + *keyp;
1353 /* Skip the data associated with this old key. */
1354 if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
1356 ndp = data_skip(dp, REPOKEY_TYPE_ID);
1357 ndp = data_skip(ndp, REPOKEY_TYPE_ID);
1359 else if (key->storage == KEY_STORAGE_INCORE)
1360 ndp = data_skip(dp, key->type);
1363 if (seen[*keyp] == -1)
1365 /* If this key was an old one _and_ was not overwritten with
1366 a different value copy over the old value (we skipped it
1369 data_addblob(&newincore, dp, ndp - dp);
1372 else if (seen[*keyp])
1374 /* Otherwise we have a new value. Parse it into the internal
1377 unsigned int oldvincorelen = 0;
1380 if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
1383 oldvincorelen = xd->len;
1385 id = seen[*keyp] - 1;
1388 case REPOKEY_TYPE_VOID:
1389 case REPOKEY_TYPE_CONSTANT:
1390 case REPOKEY_TYPE_CONSTANTID:
1392 case REPOKEY_TYPE_STR:
1393 data_addblob(xd, data->attrdata + id, strlen((char *)(data->attrdata + id)) + 1);
1395 case REPOKEY_TYPE_ID:
1396 case REPOKEY_TYPE_NUM:
1397 case REPOKEY_TYPE_DIR:
1400 case REPOKEY_TYPE_DIRNUMNUMARRAY:
1401 for (ida = data->attriddata + id; *ida; ida += 3)
1403 data_addid(xd, ida[0]);
1404 data_addid(xd, ida[1]);
1405 data_addideof(xd, ida[2], ida[3] ? 0 : 1);
1408 case REPOKEY_TYPE_DIRSTRARRAY:
1409 for (ida = data->attriddata + id; *ida; ida += 2)
1411 data_addideof(xd, ida[0], ida[2] ? 0 : 1);
1412 data_addblob(xd, data->attrdata + ida[1], strlen((char *)(data->attrdata + ida[1])) + 1);
1416 fprintf(stderr, "don't know how to handle type %d\n", key->type);
1419 if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
1421 /* put offset/len in incore */
1422 data_addid(&newincore, data->lastverticaloffset + oldvincorelen);
1423 oldvincorelen = xd->len - oldvincorelen;
1424 data_addid(&newincore, oldvincorelen);
1429 if (data->attrs[entry])
1430 sat_free(data->attrs[entry]);
1435 sat_free(data->incoredata);
1436 data->incoredata = newincore.buf;
1437 data->incoredatalen = newincore.len;
1438 data->incoredatafree = 0;
1440 sat_free(data->vincore);
1441 data->vincore = newvincore.buf;
1442 data->vincorelen = newvincore.len;
1444 data->attrs = sat_free(data->attrs);
1445 data->attrdata = sat_free(data->attrdata);
1446 data->attriddata = sat_free(data->attriddata);
1447 data->attrdatalen = 0;
1448 data->attriddatalen = 0;
1452 repodata_str2dir(Repodata *data, const char *dir, int create)
1458 while (*dir == '/' && dir[1] == '/')
1462 dire = strchrnul(dir, '/');
1463 if (data->localpool)
1464 id = stringpool_strn2id(&data->spool, dir, dire - dir, create);
1466 id = strn2id(data->repo->pool, dir, dire - dir, create);
1469 parent = dirpool_add_dir(&data->dirpool, parent, id, create);
1482 repodata_compress_page(unsigned char *page, unsigned int len, unsigned char *cpage, unsigned int max)
1484 return compress_buf(page, len, cpage, max);
1487 #define SOLV_ERROR_EOF 3
1489 static inline unsigned int
1495 for (i = 0; i < 4; i++)
1505 #define SOLV_ERROR_EOF 3
1506 #define SOLV_ERROR_CORRUPT 6
1508 /* Try to either setup on-demand paging (using FP as backing
1509 file), or in case that doesn't work (FP not seekable) slurps in
1510 all pages and deactivates paging. */
1512 repodata_read_or_setup_pages(Repodata *data, unsigned int pagesz, unsigned int blobsz)
1514 FILE *fp = data->fp;
1515 unsigned int npages;
1517 unsigned int can_seek;
1519 unsigned char buf[BLOB_PAGESIZE];
1521 if (pagesz != BLOB_PAGESIZE)
1523 /* We could handle this by slurping in everything. */
1524 data->error = SOLV_ERROR_CORRUPT;
1528 if ((cur_file_ofs = ftell(fp)) < 0)
1532 data->pagefd = dup(fileno(fp));
1533 if (data->pagefd == -1)
1537 fprintf (stderr, "can %sseek\n", can_seek ? "" : "NOT ");
1539 npages = (blobsz + BLOB_PAGESIZE - 1) / BLOB_PAGESIZE;
1541 data->num_pages = npages;
1542 data->pages = sat_malloc2(npages, sizeof(data->pages[0]));
1544 /* If we can't seek on our input we have to slurp in everything. */
1546 data->blob_store = sat_malloc(npages * BLOB_PAGESIZE);
1547 for (i = 0; i < npages; i++)
1549 unsigned int in_len = read_u32(fp);
1550 unsigned int compressed = in_len & 1;
1551 Attrblobpage *p = data->pages + i;
1554 fprintf (stderr, "page %d: len %d (%scompressed)\n",
1555 i, in_len, compressed ? "" : "not ");
1561 p->file_offset = cur_file_ofs;
1562 p->file_size = in_len * 2 + compressed;
1563 if (fseek(fp, in_len, SEEK_CUR) < 0)
1566 fprintf (stderr, "can't seek after we thought we can\n");
1567 /* We can't fall back to non-seeking behaviour as we already
1568 read over some data pages without storing them away. */
1569 data->error = SOLV_ERROR_EOF;
1570 close(data->pagefd);
1574 cur_file_ofs += in_len;
1578 unsigned int out_len;
1579 void *dest = data->blob_store + i * BLOB_PAGESIZE;
1580 p->mapped_at = i * BLOB_PAGESIZE;
1583 /* We can't seek, so suck everything in. */
1584 if (fread(compressed ? buf : dest, in_len, 1, fp) != 1)
1587 data->error = SOLV_ERROR_EOF;
1592 out_len = unchecked_decompress_buf(buf, in_len, dest, BLOB_PAGESIZE);
1593 if (out_len != BLOB_PAGESIZE && i < npages - 1)
1595 data->error = SOLV_ERROR_CORRUPT;
1604 vim:cinoptions={.5s,g0,p5,t0,(0,^-0.5s,n-0.5s:tw=78:cindent:sw=4: