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"
29 extern unsigned int compress_buf (const unsigned char *in, unsigned int in_len,
30 unsigned char *out, unsigned int out_len);
31 extern unsigned int unchecked_decompress_buf (const unsigned char *in,
34 unsigned int out_len);
36 #define REPODATA_BLOCK 255
39 repodata_free(Repodata *data)
42 sat_free(data->schemata);
43 sat_free(data->schemadata);
45 sat_free(data->spool.strings);
46 sat_free(data->spool.stringspace);
47 sat_free(data->spool.stringhashtbl);
49 sat_free(data->dirpool.dirs);
50 sat_free(data->dirpool.dirtraverse);
52 sat_free(data->incoredata);
53 sat_free(data->incoreoffset);
54 sat_free(data->verticaloffset);
56 sat_free(data->blob_store);
57 sat_free(data->pages);
58 sat_free(data->mapped);
60 sat_free(data->vincore);
62 sat_free(data->attrs);
63 sat_free(data->attrdata);
64 sat_free(data->attriddata);
70 static unsigned char *
71 data_read_id(unsigned char *dp, Id *idp)
83 x = (x << 7) ^ c ^ 128;
87 static unsigned char *
88 data_read_ideof(unsigned char *dp, Id *idp, int *eof)
107 x = (x << 7) ^ c ^ 128;
111 static unsigned char *
112 data_skip(unsigned char *dp, int type)
123 while ((*dp & 0x80) != 0)
127 while ((*dp & 0xc0) != 0)
134 case TYPE_DIRSTRARRAY:
137 while ((*dp & 0x80) != 0)
146 case TYPE_DIRNUMNUMARRAY:
149 while ((*dp & 0x80) != 0)
152 while ((*dp & 0x80) != 0)
155 while ((*dp & 0x80) != 0)
162 fprintf(stderr, "unknown type in data_skip\n");
167 static unsigned char *
168 data_fetch(unsigned char *dp, KeyValue *kv, Repokey *key)
181 kv->str = (const char *)dp;
182 return dp + strlen(kv->str) + 1;
184 return data_read_id(dp, &kv->id);
186 return data_read_id(dp, &kv->num);
188 return data_read_ideof(dp, &kv->id, &kv->eof);
190 return data_read_id(dp, &kv->id);
191 case TYPE_DIRSTRARRAY:
192 dp = data_read_ideof(dp, &kv->id, &kv->eof);
193 kv->str = (const char *)dp;
194 return dp + strlen(kv->str) + 1;
195 case TYPE_DIRNUMNUMARRAY:
196 dp = data_read_id(dp, &kv->id);
197 dp = data_read_id(dp, &kv->num);
198 return data_read_ideof(dp, &kv->num2, &kv->eof);
204 static unsigned char *
205 forward_to_key(Repodata *data, Id key, Id schemaid, unsigned char *dp)
209 keyp = data->schemadata + data->schemata[schemaid];
210 while ((k = *keyp++) != 0)
214 if (data->keys[k].storage == KEY_STORAGE_VERTICAL_OFFSET)
216 dp = data_skip(dp, TYPE_ID); /* skip that offset */
217 dp = data_skip(dp, TYPE_ID); /* skip that length */
220 if (data->keys[k].storage != KEY_STORAGE_INCORE)
222 dp = data_skip(dp, data->keys[k].type);
227 #define BLOB_PAGEBITS 15
228 #define BLOB_PAGESIZE (1 << BLOB_PAGEBITS)
230 static unsigned char *
231 load_page_range(Repodata *data, unsigned int pstart, unsigned int pend)
233 /* Make sure all pages from PSTART to PEND (inclusive) are loaded,
234 and are consecutive. Return a pointer to the mapping of PSTART. */
235 unsigned char buf[BLOB_PAGESIZE];
238 /* Quick check in case all pages are there already and consecutive. */
239 for (i = pstart; i <= pend; i++)
240 if (data->pages[i].mapped_at == -1
242 && data->pages[i].mapped_at
243 != data->pages[i-1].mapped_at + BLOB_PAGESIZE))
246 return data->blob_store + data->pages[pstart].mapped_at;
248 /* Ensure that we can map the numbers of pages we need at all. */
249 if (pend - pstart + 1 > data->ncanmap)
251 unsigned int oldcan = data->ncanmap;
252 data->ncanmap = pend - pstart + 1;
253 if (data->ncanmap < 4)
255 data->mapped = sat_realloc2(data->mapped, data->ncanmap, sizeof(data->mapped[0]));
256 memset (data->mapped + oldcan, 0, (data->ncanmap - oldcan) * sizeof (data->mapped[0]));
257 data->blob_store = sat_realloc2(data->blob_store, data->ncanmap, BLOB_PAGESIZE);
259 fprintf (stderr, "PAGE: can map %d pages\n", data->ncanmap);
263 /* Now search for "cheap" space in our store. Space is cheap if it's either
264 free (very cheap) or contains pages we search for anyway. */
266 /* Setup cost array. */
267 unsigned int cost[data->ncanmap];
268 for (i = 0; i < data->ncanmap; i++)
270 unsigned int pnum = data->mapped[i];
276 Attrblobpage *p = data->pages + pnum;
277 assert (p->mapped_at != -1);
278 if (pnum >= pstart && pnum <= pend)
285 /* And search for cheapest space. */
286 unsigned int best_cost = -1;
287 unsigned int best = 0;
288 unsigned int same_cost = 0;
289 for (i = 0; i + pend - pstart < data->ncanmap; i++)
291 unsigned int c = cost[i];
293 for (j = 0; j < pend - pstart + 1; j++)
296 best_cost = c, best = i;
297 else if (c == best_cost)
299 /* A null cost won't become better. */
303 /* If all places have the same cost we would thrash on slot 0. Avoid
304 this by doing a round-robin strategy in this case. */
305 if (same_cost == data->ncanmap - pend + pstart - 1)
306 best = data->rr_counter++ % (data->ncanmap - pend + pstart);
308 /* So we want to map our pages from [best] to [best+pend-pstart].
309 Use a very simple strategy, which doesn't make the best use of
310 our resources, but works. Throw away all pages in that range
311 (even ours) then copy around ours (in case they were outside the
312 range) or read them in. */
313 for (i = best; i < best + pend - pstart + 1; i++)
315 unsigned int pnum = data->mapped[i];
317 /* If this page is exactly at the right place already,
318 no need to evict it. */
319 && pnum != pstart + i - best)
321 /* Evict this page. */
323 fprintf (stderr, "PAGE: evict page %d from %d\n", pnum, i);
327 data->pages[pnum].mapped_at = -1;
331 /* Everything is free now. Read in the pages we want. */
332 for (i = pstart; i <= pend; i++)
334 Attrblobpage *p = data->pages + i;
335 unsigned int pnum = i - pstart + best;
336 void *dest = data->blob_store + pnum * BLOB_PAGESIZE;
337 if (p->mapped_at != -1)
339 if (p->mapped_at != pnum * BLOB_PAGESIZE)
342 fprintf (stderr, "PAGECOPY: %d to %d\n", i, pnum);
344 /* Still mapped somewhere else, so just copy it from there. */
345 memcpy (dest, data->blob_store + p->mapped_at, BLOB_PAGESIZE);
346 data->mapped[p->mapped_at / BLOB_PAGESIZE] = 0;
351 unsigned int in_len = p->file_size;
352 unsigned int compressed = in_len & 1;
355 fprintf (stderr, "PAGEIN: %d to %d", i, pnum);
357 /* Not mapped, so read in this page. */
358 if (fseek(data->fp, p->file_offset, SEEK_SET) < 0)
360 perror ("mapping fseek");
363 if (fread(compressed ? buf : dest, in_len, 1, data->fp) != 1)
365 perror ("mapping fread");
370 unsigned int out_len;
371 out_len = unchecked_decompress_buf(buf, in_len,
372 dest, BLOB_PAGESIZE);
373 if (out_len != BLOB_PAGESIZE
374 && i < data->num_pages - 1)
376 fprintf (stderr, "can't decompress\n");
380 fprintf (stderr, " (expand %d to %d)", in_len, out_len);
384 fprintf (stderr, "\n");
387 p->mapped_at = pnum * BLOB_PAGESIZE;
388 data->mapped[pnum] = i + 1;
390 return data->blob_store + best * BLOB_PAGESIZE;
393 static unsigned char *
394 make_vertical_available(Repodata *data, Repokey *key, Id off, Id len)
397 if (key->type == TYPE_VOID)
399 if (off >= data->lastverticaloffset)
401 off -= data->lastverticaloffset;
402 if (off + len > data->vincorelen)
404 return data->vincore + off;
408 if (off + len > key->size)
410 /* we now have the offset, go into vertical */
411 off += data->verticaloffset[key - data->keys];
412 dp = load_page_range(data, off / BLOB_PAGESIZE, (off + len - 1) / BLOB_PAGESIZE);
414 dp += off % BLOB_PAGESIZE;
418 static inline unsigned char *
419 get_data(Repodata *data, Repokey *key, unsigned char **dpp)
421 unsigned char *dp = *dpp;
425 if (key->storage == KEY_STORAGE_INCORE)
427 /* hmm, this is a bit expensive */
428 *dpp = data_skip(dp, key->type);
431 else if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
434 dp = data_read_id(dp, &off);
435 dp = data_read_id(dp, &len);
437 return make_vertical_available(data, key, off, len);
443 maybe_load_repodata(Repodata *data)
445 if (data->state == REPODATA_STUB)
447 if (data->loadcallback)
448 data->loadcallback(data);
450 data->state = REPODATA_ERROR;
452 if (data->state == REPODATA_AVAILABLE)
454 data->state = REPODATA_ERROR;
459 repodata_lookup_str(Repodata *data, Id entry, Id keyid)
466 if (!maybe_load_repodata (data))
469 dp = data->incoredata + data->incoreoffset[entry];
470 dp = data_read_id(dp, &schema);
471 /* make sure the schema of this solvable contains the key */
472 for (keyp = data->schemadata + data->schemata[schema]; *keyp != keyid; keyp++)
475 dp = forward_to_key(data, keyid, schema, dp);
476 key = data->keys + keyid;
477 dp = get_data(data, key, &dp);
480 if (key->type == TYPE_STR)
481 return (const char *)dp;
482 if (key->type != TYPE_ID)
484 /* id type, must either use global or local string store*/
485 dp = data_read_id(dp, &id);
487 return data->spool.stringspace + data->spool.strings[id];
488 return id2str(data->repo->pool, id);
492 repodata_lookup_num(Repodata *data, Id entry, Id keyid, unsigned *value)
502 if (!maybe_load_repodata (data))
505 dp = data->incoredata + data->incoreoffset[entry];
506 dp = data_read_id(dp, &schema);
507 /* make sure the schema of this solvable contains the key */
508 for (keyp = data->schemadata + data->schemata[schema]; *keyp != keyid; keyp++)
511 dp = forward_to_key(data, keyid, schema, dp);
512 key = data->keys + keyid;
513 dp = get_data(data, key, &dp);
516 if (key->type == TYPE_NUM
517 || key->type == TYPE_U32
518 || key->type == TYPE_CONSTANT)
520 dp = data_fetch(dp, &kv, key);
528 repodata_search(Repodata *data, Id entry, Id keyname, int (*callback)(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv), void *cbdata)
532 Id k, keyid, *kp, *keyp;
533 unsigned char *dp, *ddp;
538 if (!maybe_load_repodata (data))
541 dp = data->incoredata + data->incoreoffset[entry];
542 dp = data_read_id(dp, &schema);
543 keyp = data->schemadata + data->schemata[schema];
546 /* search in a specific key */
547 for (kp = keyp; (k = *kp++) != 0; )
548 if (data->keys[k].name == keyname)
552 dp = forward_to_key(data, k, schema, dp);
558 while ((keyid = *keyp++) != 0)
561 key = data->keys + keyid;
562 ddp = get_data(data, key, &dp);
565 ddp = data_fetch(ddp, &kv, key);
568 stop = callback(cbdata, data->repo->pool->solvables + data->start + entry, data, key, &kv);
570 while (!kv.eof && !stop);
571 if (onekey || stop > SEARCH_NEXT_KEY)
577 dataiterator_newdata(Dataiterator *di)
579 Id keyname = di->keyname;
580 Repodata *data = di->data;
583 if (data->state == REPODATA_STUB)
588 for (j = 1; j < data->nkeys; j++)
589 if (keyname == data->keys[j].name)
591 if (j == data->nkeys)
595 if (data->loadcallback)
596 data->loadcallback(data);
598 data->state = REPODATA_ERROR;
600 if (data->state == REPODATA_ERROR)
604 unsigned char *dp = data->incoredata + data->incoreoffset[di->solvid - data->start];
605 dp = data_read_id(dp, &schema);
606 Id *keyp = data->schemadata + data->schemata[schema];
610 /* search in a specific key */
611 for (kp = keyp; (k = *kp++) != 0; )
612 if (data->keys[k].name == keyname)
616 dp = forward_to_key(data, k, schema, dp);
626 di->key = di->data->keys + keyid;
631 di->dp = get_data(di->data, di->key, &di->nextkeydp);
636 dataiterator_init(Dataiterator *di, Repo *repo, Id p, Id keyname,
637 const char *match, int flags)
643 di->flags |= __SEARCH_ONESOLVABLE;
644 di->data = repo->repodata - 1;
645 if (flags & SEARCH_NO_STORAGE_SOLVABLE)
652 di->solvid = repo->start - 1;
653 di->data = repo->repodata + repo->nrepodata - 1;
657 di->keyname = keyname;
658 static Id zeroid = 0;
665 /* FIXME factor and merge with repo_matchvalue */
667 dataiterator_match(Dataiterator *di, KeyValue *kv)
669 int flags = di->flags;
671 if ((flags & SEARCH_STRINGMASK) != 0)
673 switch (di->key->type)
677 if (di->data && di->data->localpool)
678 kv->str = stringpool_id2str(&di->data->spool, kv->id);
680 kv->str = id2str(di->repo->pool, kv->id);
687 switch ((flags & SEARCH_STRINGMASK))
689 case SEARCH_SUBSTRING:
690 if (flags & SEARCH_NOCASE)
692 if (!strcasestr(kv->str, di->match))
697 if (!strstr(kv->str, di->match))
702 if (flags & SEARCH_NOCASE)
704 if (strcasecmp(di->match, kv->str))
709 if (strcmp(di->match, kv->str))
714 if (fnmatch(di->match, kv->str, (flags & SEARCH_NOCASE) ? FNM_CASEFOLD : 0))
719 if (regexec(&di->regexp, kv->str, 0, NULL, 0))
729 static Repokey solvablekeys[RPM_RPMDBID - SOLVABLE_NAME + 1] = {
730 { SOLVABLE_NAME, TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
731 { SOLVABLE_ARCH, TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
732 { SOLVABLE_EVR, TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
733 { SOLVABLE_VENDOR, TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
734 { SOLVABLE_PROVIDES, TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
735 { SOLVABLE_OBSOLETES, TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
736 { SOLVABLE_CONFLICTS, TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
737 { SOLVABLE_REQUIRES, TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
738 { SOLVABLE_RECOMMENDS, TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
739 { SOLVABLE_SUGGESTS, TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
740 { SOLVABLE_SUPPLEMENTS, TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
741 { SOLVABLE_ENHANCES, TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
742 { SOLVABLE_FRESHENS, TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
743 { RPM_RPMDBID, TYPE_U32, 0, KEY_STORAGE_SOLVABLE },
747 dataiterator_step(Dataiterator *di)
761 di->kv.eof = idp[1] ? 0 : 1;
767 Solvable *s = di->repo->pool->solvables + di->solvid;
768 int state = di->state;
769 di->key = solvablekeys + state - 1;
771 di->state = RPM_RPMDBID;
778 state = di->keyname - 1;
797 case SOLVABLE_VENDOR:
800 di->kv.id = s->vendor;
802 case SOLVABLE_PROVIDES:
803 di->idp = s->provides
804 ? di->repo->idarraydata + s->provides : 0;
806 case SOLVABLE_OBSOLETES:
807 di->idp = s->obsoletes
808 ? di->repo->idarraydata + s->obsoletes : 0;
810 case SOLVABLE_CONFLICTS:
811 di->idp = s->conflicts
812 ? di->repo->idarraydata + s->conflicts : 0;
814 case SOLVABLE_REQUIRES:
815 di->idp = s->requires
816 ? di->repo->idarraydata + s->requires : 0;
818 case SOLVABLE_RECOMMENDS:
819 di->idp = s->recommends
820 ? di->repo->idarraydata + s->recommends : 0;
822 case SOLVABLE_SUPPLEMENTS:
823 di->idp = s->supplements
824 ? di->repo->idarraydata + s->supplements : 0;
826 case SOLVABLE_SUGGESTS:
827 di->idp = s->suggests
828 ? di->repo->idarraydata + s->suggests : 0;
830 case SOLVABLE_ENHANCES:
831 di->idp = s->enhances
832 ? di->repo->idarraydata + s->enhances : 0;
834 case SOLVABLE_FRESHENS:
835 di->idp = s->freshens
836 ? di->repo->idarraydata + s->freshens : 0;
839 if (!di->repo->rpmdbid)
841 di->kv.num = di->repo->rpmdbid[di->solvid - di->repo->start];
844 di->data = di->repo->repodata - 1;
855 di->dp = data_fetch(di->dp, &di->kv, di->key);
860 if (di->keyname || !(keyid = *di->keyp++))
864 Repo *repo = di->repo;
865 Repodata *data = ++di->data;
866 if (data >= repo->repodata + repo->nrepodata)
868 if (di->flags & __SEARCH_ONESOLVABLE)
870 while (++di->solvid < repo->end)
871 if (repo->pool->solvables[di->solvid].repo == repo)
873 if (di->solvid >= repo->end)
875 di->data = repo->repodata - 1;
876 if (di->flags & SEARCH_NO_STORAGE_SOLVABLE)
878 static Id zeroid = 0;
883 if (di->solvid >= data->start && di->solvid < data->end)
885 dataiterator_newdata(di);
893 di->key = di->data->keys + keyid;
894 di->dp = get_data(di->data, di->key, &di->nextkeydp);
896 di->dp = data_fetch(di->dp, &di->kv, di->key);
901 || dataiterator_match(di, &di->kv))
908 repodata_init(Repodata *data, Repo *repo, int localpool)
910 memset(data, 0, sizeof (*data));
912 data->localpool = localpool;
914 stringpool_init_empty(&data->spool);
915 data->keys = sat_calloc(1, sizeof(Repokey));
917 data->schemata = sat_calloc(1, sizeof(Id));
918 data->schemadata = sat_calloc(1, sizeof(Id));
920 data->schemadatalen = 1;
921 data->start = repo->start;
922 data->end = repo->end;
923 data->incoreoffset = sat_extend_resize(0, data->end - data->start, sizeof(Id), REPODATA_BLOCK);
926 /* extend repodata so that it includes solvables p */
928 repodata_extend(Repodata *data, Id p)
930 if (data->start == data->end)
931 data->start = data->end = p;
934 int old = data->end - data->start;
935 int new = p - data->end + 1;
938 data->attrs = sat_extend(data->attrs, old, new, sizeof(Id *), REPODATA_BLOCK);
939 memset(data->attrs + old, 0, new * sizeof(Id *));
941 data->incoreoffset = sat_extend(data->incoreoffset, old, new, sizeof(Id), REPODATA_BLOCK);
942 memset(data->incoreoffset + old, 0, new * sizeof(Id));
947 int old = data->end - data->start;
948 int new = data->start - p;
951 data->attrs = sat_extend_resize(data->attrs, old + new, sizeof(Id *), REPODATA_BLOCK);
952 memmove(data->attrs + new, data->attrs, old * sizeof(Id *));
953 memset(data->attrs, 0, new * sizeof(Id *));
955 data->incoreoffset = sat_extend_resize(data->incoreoffset, old + new, sizeof(Id), REPODATA_BLOCK);
956 memmove(data->incoreoffset + new, data->incoreoffset, old * sizeof(Id));
957 memset(data->incoreoffset, 0, new * sizeof(Id));
963 repodata_extend_block(Repodata *data, Id start, Id num)
967 if (!data->incoreoffset)
969 data->incoreoffset = sat_extend_resize(data->incoreoffset, num, sizeof(Id), REPODATA_BLOCK);
970 memset(data->incoreoffset, 0, num * sizeof(Id));
972 data->end = start + num;
975 repodata_extend(data, start);
977 repodata_extend(data, start + num - 1);
980 #define REPODATA_ATTRS_BLOCK 63
981 #define REPODATA_ATTRDATA_BLOCK 1023
982 #define REPODATA_ATTRIDDATA_BLOCK 63
985 repodata_insert_keyid(Repodata *data, Id entry, Id keyid, Id val, int overwrite)
991 data->attrs = sat_extend_resize(0, data->end - data->start, sizeof(Id *), REPODATA_BLOCK);
992 memset(data->attrs, 0, (data->end - data->start) * sizeof(Id *));
995 if (data->attrs[entry])
997 for (pp = data->attrs[entry]; *pp; pp += 2)
998 /* Determine equality based on the name only, allows us to change
999 type (when overwrite is set), and makes TYPE_CONSTANT work. */
1000 if (data->keys[*pp].name == data->keys[keyid].name)
1011 i = pp - data->attrs[entry];
1013 data->attrs[entry] = sat_extend(data->attrs[entry], i, 3, sizeof(Id), REPODATA_ATTRS_BLOCK);
1014 pp = data->attrs[entry] + i;
1021 repodata_set(Repodata *data, Id entry, Repokey *key, Id val)
1025 /* find key in keys */
1026 for (keyid = 1; keyid < data->nkeys; keyid++)
1027 if (data->keys[keyid].name == key->name && data->keys[keyid].type == key->type)
1029 if (key->type == TYPE_CONSTANT && key->size != data->keys[keyid].size)
1033 if (keyid == data->nkeys)
1035 /* allocate new key */
1036 data->keys = sat_realloc2(data->keys, data->nkeys + 1, sizeof(Repokey));
1037 data->keys[data->nkeys++] = *key;
1038 if (data->verticaloffset)
1040 data->verticaloffset = sat_realloc2(data->verticaloffset, data->nkeys, sizeof(Id));
1041 data->verticaloffset[data->nkeys - 1] = 0;
1044 repodata_insert_keyid(data, entry, keyid, val, 1);
1048 repodata_set_id(Repodata *data, Id entry, Id keyname, Id id)
1054 key.storage = KEY_STORAGE_INCORE;
1055 repodata_set(data, entry, &key, id);
1059 repodata_set_num(Repodata *data, Id entry, Id keyname, Id num)
1063 key.type = TYPE_NUM;
1065 key.storage = KEY_STORAGE_INCORE;
1066 repodata_set(data, entry, &key, num);
1070 repodata_set_poolstr(Repodata *data, Id entry, Id keyname, const char *str)
1074 if (data->localpool)
1075 id = stringpool_str2id(&data->spool, str, 1);
1077 id = str2id(data->repo->pool, str, 1);
1081 key.storage = KEY_STORAGE_INCORE;
1082 repodata_set(data, entry, &key, id);
1086 repodata_set_constant(Repodata *data, Id entry, Id keyname, Id constant)
1090 key.type = TYPE_CONSTANT;
1091 key.size = constant;
1092 key.storage = KEY_STORAGE_INCORE;
1093 repodata_set(data, entry, &key, 0);
1097 repodata_set_void(Repodata *data, Id entry, Id keyname)
1101 key.type = TYPE_VOID;
1103 key.storage = KEY_STORAGE_INCORE;
1104 repodata_set(data, entry, &key, 0);
1108 repodata_set_str(Repodata *data, Id entry, Id keyname, const char *str)
1113 l = strlen(str) + 1;
1115 key.type = TYPE_STR;
1117 key.storage = KEY_STORAGE_INCORE;
1118 data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
1119 memcpy(data->attrdata + data->attrdatalen, str, l);
1120 repodata_set(data, entry, &key, data->attrdatalen);
1121 data->attrdatalen += l;
1125 repodata_add_dirnumnum(Repodata *data, Id entry, Id keyname, Id dir, Id num, Id num2)
1131 fprintf(stderr, "repodata_add_dirnumnum %d %d %d %d (%d)\n", entry, dir, num, num2, data->attriddatalen);
1133 if (data->attrs && data->attrs[entry])
1135 for (pp = data->attrs[entry]; *pp; pp += 2)
1136 if (data->keys[*pp].name == keyname && data->keys[*pp].type == TYPE_DIRNUMNUMARRAY)
1141 for (ida = data->attriddata + pp[1]; *ida; ida += 3)
1143 if (ida + 1 == data->attriddata + data->attriddatalen)
1145 /* this was the last entry, just append it */
1146 data->attriddata = sat_extend(data->attriddata, data->attriddatalen, 3, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1147 data->attriddatalen--; /* overwrite terminating 0 */
1151 /* too bad. move to back. */
1152 data->attriddata = sat_extend(data->attriddata, data->attriddatalen, oldsize + 4, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1153 memcpy(data->attriddata + data->attriddatalen, data->attriddata + pp[1], oldsize * sizeof(Id));
1154 pp[1] = data->attriddatalen;
1155 data->attriddatalen += oldsize;
1157 data->attriddata[data->attriddatalen++] = dir;
1158 data->attriddata[data->attriddatalen++] = num;
1159 data->attriddata[data->attriddatalen++] = num2;
1160 data->attriddata[data->attriddatalen++] = 0;
1165 key.type = TYPE_DIRNUMNUMARRAY;
1167 key.storage = KEY_STORAGE_INCORE;
1168 data->attriddata = sat_extend(data->attriddata, data->attriddatalen, 4, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1169 repodata_set(data, entry, &key, data->attriddatalen);
1170 data->attriddata[data->attriddatalen++] = dir;
1171 data->attriddata[data->attriddatalen++] = num;
1172 data->attriddata[data->attriddatalen++] = num2;
1173 data->attriddata[data->attriddatalen++] = 0;
1177 repodata_add_dirstr(Repodata *data, Id entry, Id keyname, Id dir, const char *str)
1179 Id *ida, *pp, stroff;
1183 l = strlen(str) + 1;
1184 data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
1185 memcpy(data->attrdata + data->attrdatalen, str, l);
1186 stroff = data->attrdatalen;
1187 data->attrdatalen += l;
1190 fprintf(stderr, "repodata_add_dirstr %d %d %s (%d)\n", entry, dir, str, data->attriddatalen);
1192 if (data->attrs && data->attrs[entry])
1194 for (pp = data->attrs[entry]; *pp; pp += 2)
1195 if (data->keys[*pp].name == keyname && data->keys[*pp].type == TYPE_DIRSTRARRAY)
1200 for (ida = data->attriddata + pp[1]; *ida; ida += 2)
1202 if (ida + 1 == data->attriddata + data->attriddatalen)
1204 /* this was the last entry, just append it */
1205 data->attriddata = sat_extend(data->attriddata, data->attriddatalen, 2, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1206 data->attriddatalen--; /* overwrite terminating 0 */
1210 /* too bad. move to back. */
1211 data->attriddata = sat_extend(data->attriddata, data->attriddatalen, oldsize + 3, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1212 memcpy(data->attriddata + data->attriddatalen, data->attriddata + pp[1], oldsize * sizeof(Id));
1213 pp[1] = data->attriddatalen;
1214 data->attriddatalen += oldsize;
1216 data->attriddata[data->attriddatalen++] = dir;
1217 data->attriddata[data->attriddatalen++] = stroff;
1218 data->attriddata[data->attriddatalen++] = 0;
1223 key.type = TYPE_DIRSTRARRAY;
1225 key.storage = KEY_STORAGE_INCORE;
1226 data->attriddata = sat_extend(data->attriddata, data->attriddatalen, 3, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1227 repodata_set(data, entry, &key, data->attriddatalen);
1228 data->attriddata[data->attriddatalen++] = dir;
1229 data->attriddata[data->attriddatalen++] = stroff;
1230 data->attriddata[data->attriddatalen++] = 0;
1234 repodata_merge_attrs (Repodata *data, Id dest, Id src)
1237 for (keyp = data->attrs[src]; *keyp; keyp += 2)
1238 repodata_insert_keyid(data, dest, keyp[0], keyp[1], 0);
1241 /*********************************/
1243 /* unify with repo_write! */
1245 #define EXTDATA_BLOCK 1023
1246 #define SCHEMATA_BLOCK 31
1247 #define SCHEMATADATA_BLOCK 255
1255 data_addid(struct extdata *xd, Id x)
1258 xd->buf = sat_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
1259 dp = xd->buf + xd->len;
1264 *dp++ = (x >> 28) | 128;
1266 *dp++ = (x >> 21) | 128;
1267 *dp++ = (x >> 14) | 128;
1270 *dp++ = (x >> 7) | 128;
1272 xd->len = dp - xd->buf;
1276 data_addideof(struct extdata *xd, Id x, int eof)
1279 x = (x & 63) | ((x & ~63) << 1);
1280 data_addid(xd, (eof ? x: x | 64));
1284 data_addblob(struct extdata *xd, unsigned char *blob, int len)
1286 xd->buf = sat_extend(xd->buf, xd->len, len, 1, EXTDATA_BLOCK);
1287 memcpy(xd->buf + xd->len, blob, len);
1291 /*********************************/
1294 addschema_prepare(Repodata *data, Id *schematacache)
1299 memset(schematacache, 0, 256 * sizeof(Id));
1300 for (i = 0; i < data->nschemata; i++)
1302 for (sp = data->schemadata + data->schemata[i], h = 0; *sp; len++)
1305 schematacache[h] = i + 1;
1307 data->schemadata = sat_extend_resize(data->schemadata, data->schemadatalen, sizeof(Id), SCHEMATADATA_BLOCK);
1308 data->schemata = sat_extend_resize(data->schemata, data->nschemata, sizeof(Id), SCHEMATA_BLOCK);
1312 addschema(Repodata *data, Id *schema, Id *schematacache)
1317 for (sp = schema, len = 0, h = 0; *sp; len++)
1322 cid = schematacache[h];
1326 if (!memcmp(data->schemadata + data->schemata[cid], schema, len * sizeof(Id)))
1328 /* cache conflict */
1329 for (cid = 0; cid < data->nschemata; cid++)
1330 if (!memcmp(data->schemadata + data->schemata[cid], schema, len * sizeof(Id)))
1333 /* a new one. make room. */
1334 data->schemadata = sat_extend(data->schemadata, data->schemadatalen, len, sizeof(Id), SCHEMATADATA_BLOCK);
1335 data->schemata = sat_extend(data->schemata, data->nschemata, 1, sizeof(Id), SCHEMATA_BLOCK);
1337 memcpy(data->schemadata + data->schemadatalen, schema, len * sizeof(Id));
1338 data->schemata[data->nschemata] = data->schemadatalen;
1339 data->schemadatalen += len;
1340 schematacache[h] = data->nschemata + 1;
1342 fprintf(stderr, "addschema: new schema\n");
1344 return data->nschemata++;
1349 repodata_internalize(Repodata *data)
1352 Id id, entry, nentry, *ida;
1353 Id schematacache[256];
1354 Id schemaid, *schema, *sp, oldschema, *keyp, *seen;
1355 unsigned char *dp, *ndp;
1356 int newschema, oldcount;
1357 struct extdata newincore;
1358 struct extdata newvincore;
1363 newvincore.buf = data->vincore;
1364 newvincore.len = data->vincorelen;
1366 schema = sat_malloc2(data->nkeys, sizeof(Id));
1367 seen = sat_malloc2(data->nkeys, sizeof(Id));
1369 /* Merge the data already existing (in data->schemata, ->incoredata and
1370 friends) with the new attributes in data->attrs[]. */
1371 nentry = data->end - data->start;
1372 addschema_prepare(data, schematacache);
1373 memset(&newincore, 0, sizeof(newincore));
1374 for (entry = 0; entry < nentry; entry++)
1376 memset(seen, 0, data->nkeys * sizeof(Id));
1378 dp = data->incoredata + data->incoreoffset[entry];
1379 if (data->incoredata)
1380 dp = data_read_id(dp, &oldschema);
1384 fprintf(stderr, "oldschema %d\n", oldschema);
1385 fprintf(stderr, "schemata %d\n", data->schemata[oldschema]);
1386 fprintf(stderr, "schemadata %p\n", data->schemadata);
1388 /* seen: -1: old data 0: skipped >0: id + 1 */
1391 for (keyp = data->schemadata + data->schemata[oldschema]; *keyp; keyp++)
1395 fprintf(stderr, "Inconsistent old data (key occured twice).\n");
1402 if (data->attrs[entry])
1403 for (keyp = data->attrs[entry]; *keyp; keyp += 2)
1410 seen[*keyp] = keyp[1] + 1;
1414 /* Ideally we'd like to sort the new schema here, to ensure
1415 schema equality independend of the ordering. We can't do that
1416 yet. For once see below (old ids need to come before new ids).
1417 An additional difficulty is that we also need to move
1418 the values with the keys. */
1419 schemaid = addschema(data, schema, schematacache);
1421 schemaid = oldschema;
1424 /* Now create data blob. We walk through the (possibly new) schema
1425 and either copy over old data, or insert the new. */
1426 /* XXX Here we rely on the fact that the (new) schema has the form
1427 o1 o2 o3 o4 ... | n1 n2 n3 ...
1428 (oX being the old keyids (possibly overwritten), and nX being
1429 the new keyids). This rules out sorting the keyids in order
1430 to ensure a small schema count. */
1431 data->incoreoffset[entry] = newincore.len;
1432 data_addid(&newincore, schemaid);
1433 for (keyp = data->schemadata + data->schemata[schemaid]; *keyp; keyp++)
1435 key = data->keys + *keyp;
1439 /* Skip the data associated with this old key. */
1440 if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
1442 ndp = data_skip(dp, TYPE_ID);
1443 ndp = data_skip(ndp, TYPE_ID);
1445 else if (key->storage == KEY_STORAGE_INCORE)
1446 ndp = data_skip(dp, key->type);
1449 if (seen[*keyp] == -1)
1451 /* If this key was an old one _and_ was not overwritten with
1452 a different value copy over the old value (we skipped it
1455 data_addblob(&newincore, dp, ndp - dp);
1458 else if (seen[*keyp])
1460 /* Otherwise we have a new value. Parse it into the internal
1463 unsigned int oldvincorelen = 0;
1466 if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
1469 oldvincorelen = xd->len;
1471 id = seen[*keyp] - 1;
1478 data_addblob(xd, data->attrdata + id, strlen((char *)(data->attrdata + id)) + 1);
1485 case TYPE_DIRNUMNUMARRAY:
1486 for (ida = data->attriddata + id; *ida; ida += 3)
1488 data_addid(xd, ida[0]);
1489 data_addid(xd, ida[1]);
1490 data_addideof(xd, ida[2], ida[3] ? 0 : 1);
1493 case TYPE_DIRSTRARRAY:
1494 for (ida = data->attriddata + id; *ida; ida += 2)
1496 data_addideof(xd, ida[0], ida[2] ? 0 : 1);
1497 data_addblob(xd, data->attrdata + ida[1], strlen((char *)(data->attrdata + ida[1])) + 1);
1501 fprintf(stderr, "don't know how to handle type %d\n", key->type);
1504 if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
1506 /* put offset/len in incore */
1507 data_addid(&newincore, data->lastverticaloffset + oldvincorelen);
1508 oldvincorelen = xd->len - oldvincorelen;
1509 data_addid(&newincore, oldvincorelen);
1514 if (data->attrs[entry])
1515 sat_free(data->attrs[entry]);
1520 sat_free(data->incoredata);
1521 data->incoredata = newincore.buf;
1522 data->incoredatalen = newincore.len;
1523 data->incoredatafree = 0;
1525 sat_free(data->vincore);
1526 data->vincore = newvincore.buf;
1527 data->vincorelen = newvincore.len;
1529 data->attrs = sat_free(data->attrs);
1530 data->attrdata = sat_free(data->attrdata);
1531 data->attriddata = sat_free(data->attriddata);
1532 data->attrdatalen = 0;
1533 data->attriddatalen = 0;
1537 repodata_str2dir(Repodata *data, const char *dir, int create)
1543 while (*dir == '/' && dir[1] == '/')
1547 dire = strchrnul(dir, '/');
1548 if (data->localpool)
1549 id = stringpool_strn2id(&data->spool, dir, dire - dir, create);
1551 id = strn2id(data->repo->pool, dir, dire - dir, create);
1554 parent = dirpool_add_dir(&data->dirpool, parent, id, create);
1567 repodata_compress_page(unsigned char *page, unsigned int len, unsigned char *cpage, unsigned int max)
1569 return compress_buf(page, len, cpage, max);
1572 #define SOLV_ERROR_EOF 3
1574 static inline unsigned int
1580 for (i = 0; i < 4; i++)
1590 /* Try to either setup on-demand paging (using FP as backing
1591 file), or in case that doesn't work (FP not seekable) slurps in
1592 all pages and deactivates paging. */
1595 repodata_read_or_setup_pages(Repodata *data, unsigned int pagesz, unsigned int blobsz)
1597 FILE *fp = data->fp;
1598 unsigned int npages;
1600 unsigned int can_seek;
1602 unsigned char buf[BLOB_PAGESIZE];
1603 if (pagesz != BLOB_PAGESIZE)
1605 /* We could handle this by slurping in everything. */
1606 fprintf (stderr, "non matching page size\n");
1610 if ((cur_file_ofs = ftell(fp)) < 0)
1614 fprintf (stderr, "can %sseek\n", can_seek ? "" : "NOT ");
1616 npages = (blobsz + BLOB_PAGESIZE - 1) / BLOB_PAGESIZE;
1618 data->num_pages = npages;
1619 data->pages = sat_malloc2(npages, sizeof(data->pages[0]));
1621 /* If we can't seek on our input we have to slurp in everything. */
1623 data->blob_store = sat_malloc(npages * BLOB_PAGESIZE);
1624 for (i = 0; i < npages; i++)
1626 unsigned int in_len = read_u32(fp);
1627 unsigned int compressed = in_len & 1;
1628 Attrblobpage *p = data->pages + i;
1631 fprintf (stderr, "page %d: len %d (%scompressed)\n",
1632 i, in_len, compressed ? "" : "not ");
1638 p->file_offset = cur_file_ofs;
1639 p->file_size = in_len * 2 + compressed;
1640 if (fseek(fp, in_len, SEEK_CUR) < 0)
1643 fprintf (stderr, "can't seek after we thought we can\n");
1644 /* We can't fall back to non-seeking behaviour as we already
1645 read over some data pages without storing them away. */
1648 cur_file_ofs += in_len;
1652 unsigned int out_len;
1653 void *dest = data->blob_store + i * BLOB_PAGESIZE;
1654 p->mapped_at = i * BLOB_PAGESIZE;
1657 /* We can't seek, so suck everything in. */
1658 if (fread(compressed ? buf : dest, in_len, 1, fp) != 1)
1665 out_len = unchecked_decompress_buf(buf, in_len, dest, BLOB_PAGESIZE);
1666 if (out_len != BLOB_PAGESIZE
1669 fprintf (stderr, "can't decompress\n");
1678 /* If we are here we were able to seek to all page
1679 positions, so activate paging by copying FP into our structure.
1680 We dup() the file, so that our callers can fclose() it and we
1681 still have it open. But this means that we share file positions
1682 with the input filedesc. So in case our caller reads it after us,
1683 and calls back into us we might change the file position unexpectedly
1685 int fd = dup (fileno (fp));
1688 /* Jeez! What a bloody system, we can't dup() anymore. */
1692 /* XXX we don't close this yet anywhere. */
1693 data->fp = fdopen (fd, "r");
1696 /* My God! What happened now? */
1703 vim:cinoptions={.5s,g0,p5,t0,(0,^-0.5s,n-0.5s:tw=78:cindent:sw=4: