razor.c
author Kristian H?gsberg <krh@redhat.com>
Thu Sep 20 19:28:09 2007 -0400 (2007-09-20)
changeset 39 5fe9c9286cd0
parent 38 88f3ec190a3f
child 41 7eea400e19db
permissions -rw-r--r--
Split the property pool into three pools; requires, provides and packages.

This simplifies the remapping code a lot and reduces memory pressure
during import a bit.
     1 #define _GNU_SOURCE
     2 
     3 #include <stdlib.h>
     4 #include <stddef.h>
     5 #include <stdio.h>
     6 #include <string.h>
     7 #include <sys/types.h>
     8 #include <sys/stat.h>
     9 #include <sys/mman.h>
    10 #include <unistd.h>
    11 #include <fcntl.h>
    12 #include <errno.h>
    13 #include <ctype.h>
    14 
    15 #include "razor.h"
    16 
    17 #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
    18 
    19 struct array {
    20 	void *data;
    21 	int size, alloc;
    22 };
    23 
    24 struct razor_set_section {
    25 	unsigned int type;
    26 	unsigned int offset;
    27 	unsigned int size;
    28 };
    29 
    30 struct razor_set_header {
    31 	unsigned int magic;
    32 	unsigned int version;
    33 	struct razor_set_section sections[0];
    34 };
    35 
    36 #define RAZOR_MAGIC 0x7a7a7a7a
    37 #define RAZOR_VERSION 1
    38 
    39 #define RAZOR_PACKAGES 0
    40 #define RAZOR_REQUIRES 1
    41 #define RAZOR_PROVIDES 2
    42 #define RAZOR_STRING_POOL 3
    43 #define RAZOR_PACKAGE_POOL 4
    44 #define RAZOR_REQUIRES_POOL 5
    45 #define RAZOR_PROVIDES_POOL 6
    46 
    47 struct razor_package {
    48 	unsigned long name;
    49 	unsigned long version;
    50 	unsigned long requires;
    51 	unsigned long provides;
    52 };
    53 
    54 struct razor_property {
    55 	unsigned long name;
    56 	unsigned long version;
    57 	unsigned long packages;
    58 };
    59 
    60 struct razor_set {
    61 	struct array string_pool;
    62  	struct array packages;
    63 	struct array package_pool;
    64  	struct array requires;
    65  	struct array provides;
    66  	struct array requires_pool;
    67  	struct array provides_pool;
    68 	struct razor_set_header *header;
    69 };
    70 
    71 struct import_property_context {
    72 	struct array *all;
    73 	struct array package;
    74 };
    75 
    76 struct razor_importer {
    77 	struct razor_set *set;
    78 	struct array buckets;
    79 	struct import_property_context requires;
    80 	struct import_property_context provides;
    81 	struct razor_package *package;
    82 	unsigned long *requires_map;
    83 	unsigned long *provides_map;
    84 };
    85 
    86 static void
    87 array_init(struct array *array)
    88 {
    89 	memset(array, 0, sizeof *array);
    90 }
    91 
    92 static void
    93 array_release(struct array *array)
    94 {
    95 	free(array->data);
    96 }
    97 
    98 static void *
    99 array_add(struct array *array, int size)
   100 {
   101 	int alloc;
   102 	void *data, *p;
   103 
   104 	if (array->alloc > 0)
   105 		alloc = array->alloc;
   106 	else
   107 		alloc = 16;
   108 
   109 	while (alloc < array->size + size)
   110 		alloc *= 2;
   111 
   112 	if (array->alloc < alloc) {
   113 		data = realloc(array->data, alloc);
   114 		if (data == NULL)
   115 			return 0;
   116 		array->data = data;
   117 		array->alloc = alloc;
   118 	}
   119 
   120 	p = array->data + array->size;
   121 	array->size += size;
   122 
   123 	return p;
   124 }
   125 
   126 static int
   127 write_to_fd(int fd, void *p, size_t size)
   128 {
   129 	int rest, len;
   130 
   131 	rest = size;
   132 	while (rest > 0) {
   133 		len = write(fd, p, rest);
   134 		if (len < 0)
   135 			return -1;
   136 		rest -= len;
   137 	}
   138 
   139 	return 0;
   140 }
   141 
   142 static void *
   143 zalloc(size_t size)
   144 {
   145 	void *p;
   146 
   147 	p = malloc(size);
   148 	memset(p, 0, size);
   149 
   150 	return p;
   151 }
   152 
   153 struct razor_set_section razor_sections[] = {
   154 	{ RAZOR_PACKAGES,	offsetof(struct razor_set, packages) },
   155 	{ RAZOR_REQUIRES,	offsetof(struct razor_set, requires) },
   156 	{ RAZOR_PROVIDES,	offsetof(struct razor_set, provides) },
   157 	{ RAZOR_STRING_POOL,	offsetof(struct razor_set, string_pool) },
   158 	{ RAZOR_PACKAGE_POOL,	offsetof(struct razor_set, package_pool) },
   159 	{ RAZOR_REQUIRES_POOL,	offsetof(struct razor_set, requires_pool) },
   160 	{ RAZOR_PROVIDES_POOL,	offsetof(struct razor_set, provides_pool) },
   161 };
   162 
   163 struct razor_set *
   164 razor_set_create(void)
   165 {
   166 	return zalloc(sizeof(struct razor_set));
   167 }
   168 
   169 struct razor_set *
   170 razor_set_open(const char *filename)
   171 {
   172 	struct razor_set *set;
   173 	struct razor_set_section *s;
   174 	struct stat stat;
   175 	struct array *array;
   176 	int fd;
   177 
   178 	set = zalloc(sizeof *set);
   179 	fd = open(filename, O_RDONLY);
   180 	if (fstat(fd, &stat) < 0)
   181 		return NULL;
   182 	set->header = mmap(NULL, stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
   183 	if (set->header == MAP_FAILED) {
   184 		free(set);
   185 		return NULL;
   186 	}
   187 
   188 	for (s = set->header->sections; ~s->type; s++) {
   189 		if (s->type >= ARRAY_SIZE(razor_sections))
   190 			continue;
   191 		if (s->type != razor_sections[s->type].type)
   192 			continue;
   193 		array = (void *) set + razor_sections[s->type].offset;
   194 		array->data = (void *) set->header + s->offset;
   195 		array->size = s->size;
   196 		array->alloc = s->size;
   197 	}
   198 	close(fd);
   199 
   200 	return set;
   201 }
   202 
   203 void
   204 razor_set_destroy(struct razor_set *set)
   205 {
   206 	unsigned int size;
   207 	struct array *a;
   208 	int i;
   209 
   210 	if (set->header) {
   211 		for (i = 0; set->header->sections[i].type; i++)
   212 			;
   213 		size = set->header->sections[i].type;
   214 		munmap(set->header, size);
   215 	} else {
   216 		for (i = 0; i < ARRAY_SIZE(razor_sections); i++) {
   217 			a = (void *) set + razor_sections[i].offset;
   218 			free(a->data);
   219 		}
   220 	}
   221 
   222 	free(set);
   223 }
   224 
   225 static int
   226 razor_set_write(struct razor_set *set, const char *filename)
   227 {
   228 	char data[4096];
   229 	struct razor_set_header *header = (struct razor_set_header *) data;
   230 	struct array *a;
   231 	unsigned long offset;
   232 	int i, fd;
   233 
   234 	memset(data, 0, sizeof data);
   235 	header->magic = RAZOR_MAGIC;
   236 	header->version = RAZOR_VERSION;
   237 	offset = sizeof data;
   238 
   239 	for (i = 0; i < ARRAY_SIZE(razor_sections); i++) {
   240 		if (razor_sections[i].type != i)
   241 			continue;
   242 		a = (void *) set + razor_sections[i].offset;
   243 		header->sections[i].type = i;
   244 		header->sections[i].offset = offset;
   245 		header->sections[i].size = a->size;
   246 		offset += (a->size + 4095) & ~4095;
   247 	}
   248 
   249 	header->sections[i].type = ~0;
   250 	header->sections[i].offset = 0;
   251 	header->sections[i].size = 0;
   252 
   253 	fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0666);
   254 	if (fd < 0)
   255 		return -1;
   256 
   257 	write_to_fd(fd, data, sizeof data);
   258 	for (i = 0; i < ARRAY_SIZE(razor_sections); i++) {
   259 		if (razor_sections[i].type != i)
   260 			continue;
   261 		a = (void *) set + razor_sections[i].offset;
   262 		write_to_fd(fd, a->data, (a->size + 4095) & ~4095);
   263 	}
   264 
   265 	close(fd);
   266 
   267 	return 0;
   268 }
   269 
   270 static unsigned int
   271 hash_string(const char *key)
   272 {
   273 	const char *p;
   274 	unsigned int hash = 0;
   275 
   276 	for (p = key; *p; p++)
   277 		hash = (hash * 617) ^ *p;
   278 
   279 	return hash;
   280 }
   281 
   282 static unsigned long
   283 razor_importer_lookup(struct razor_importer *importer, const char *key)
   284 {
   285 	unsigned int mask, start, i;
   286 	unsigned long *b;
   287 	char *pool;
   288 
   289 	pool = importer->set->string_pool.data;
   290 	mask = importer->buckets.alloc - 1;
   291 	start = hash_string(key) * sizeof(unsigned long);
   292 
   293 	for (i = 0; i < importer->buckets.alloc; i += sizeof *b) {
   294 		b = importer->buckets.data + ((start + i) & mask);
   295 
   296 		if (*b == 0)
   297 			return 0;
   298 
   299 		if (strcmp(key, &pool[*b]) == 0)
   300 			return *b;
   301 	}
   302 
   303 	return 0;
   304 }
   305 
   306 static unsigned long
   307 add_to_string_pool(struct razor_set *set, const char *key)
   308 {
   309 	int len;
   310 	char *p;
   311 
   312 	len = strlen(key) + 1;
   313 	p = array_add(&set->string_pool, len);
   314 	memcpy(p, key, len);
   315 
   316 	return p - (char *) set->string_pool.data;
   317 }
   318 
   319 static unsigned long
   320 add_to_property_pool(struct array *pool, struct array *properties)
   321 {
   322 	unsigned long  *p;
   323 
   324 	p = array_add(properties, sizeof *p);
   325 	*p = ~0ul;
   326 	p = array_add(pool, properties->size);
   327 	memcpy(p, properties->data, properties->size);
   328 
   329 	return p - (unsigned long *) pool->data;
   330 }
   331 
   332 static void
   333 do_insert(struct razor_importer *importer, unsigned long value)
   334 {
   335 	unsigned int mask, start, i;
   336 	unsigned long *b;
   337 	const char *key;
   338 
   339 	key = (char *) importer->set->string_pool.data + value;
   340 	mask = importer->buckets.alloc - 1;
   341 	start = hash_string(key) * sizeof(unsigned long);
   342 
   343 	for (i = 0; i < importer->buckets.alloc; i += sizeof *b) {
   344 		b = importer->buckets.data + ((start + i) & mask);
   345 		if (*b == 0) {
   346 			*b = value;
   347 			break;
   348 		}
   349 	}
   350 }
   351 
   352 static unsigned long
   353 razor_importer_insert(struct razor_importer *importer, const char *key)
   354 {
   355 	unsigned long value, *buckets, *b, *end;
   356 	int alloc;
   357 
   358 	alloc = importer->buckets.alloc;
   359 	array_add(&importer->buckets, 4 * sizeof *buckets);
   360 	if (alloc != importer->buckets.alloc) {
   361 		end = importer->buckets.data + alloc;
   362 		memset(end, 0, importer->buckets.alloc - alloc);
   363 		for (b = importer->buckets.data; b < end; b++) {
   364 			value = *b;
   365 			if (value != 0) {
   366 				*b = 0;
   367 				do_insert(importer, value);
   368 			}
   369 		}
   370 	}
   371 
   372 	value = add_to_string_pool(importer->set, key);
   373 	do_insert (importer, value);
   374 
   375 	return value;
   376 }
   377 
   378 static unsigned long
   379 razor_importer_tokenize(struct razor_importer *importer, const char *string)
   380 {
   381 	unsigned long token;
   382 
   383 	if (string == NULL)
   384 		return razor_importer_tokenize(importer, "");
   385 
   386 	token = razor_importer_lookup(importer, string);
   387 	if (token != 0)
   388 		return token;
   389 
   390 	return razor_importer_insert(importer, string);
   391 }
   392 
   393 void
   394 razor_importer_begin_package(struct razor_importer *importer,
   395 			     const char *name, const char *version)
   396 {
   397 	struct razor_package *p;
   398 
   399 	p = array_add(&importer->set->packages, sizeof *p);
   400 	p->name = razor_importer_tokenize(importer, name);
   401 	p->version = razor_importer_tokenize(importer, version);
   402 
   403 	importer->package = p;
   404 	array_init(&importer->requires.package);
   405 	array_init(&importer->provides.package);
   406 }
   407 
   408 void
   409 razor_importer_finish_package(struct razor_importer *importer)
   410 {
   411 	struct razor_package *p;
   412 
   413 	p = importer->package;
   414 	p->requires = add_to_property_pool(&importer->set->requires_pool,
   415 					   &importer->requires.package);
   416 	p->provides = add_to_property_pool(&importer->set->provides_pool,
   417 					   &importer->provides.package);
   418 
   419 	array_release(&importer->requires.package);
   420 	array_release(&importer->provides.package);
   421 }
   422 
   423 static void
   424 razor_importer_add_property(struct razor_importer *importer,
   425 			    struct import_property_context *pctx,
   426 			    const char *name, const char *version)
   427 {
   428 	struct razor_property *p;
   429 	unsigned long *r;
   430 
   431 	p = array_add(pctx->all, sizeof *p);
   432 	p->name = razor_importer_tokenize(importer, name);
   433 	p->version = razor_importer_tokenize(importer, version);
   434 	p->packages = importer->package -
   435 		(struct razor_package *) importer->set->packages.data;
   436 
   437 	r = array_add(&pctx->package, sizeof *r);
   438 	*r = p - (struct razor_property *) pctx->all->data;
   439 }
   440 
   441 void
   442 razor_importer_add_requires(struct razor_importer *importer,
   443 			    const char *name, const char *version)
   444 {
   445 	razor_importer_add_property(importer,
   446 				    &importer->requires, name, version);
   447 }
   448 
   449 void
   450 razor_importer_add_provides(struct razor_importer *importer,
   451 			    const char *name, const char *version)
   452 {
   453 	razor_importer_add_property(importer,
   454 				    &importer->provides, name, version);
   455 }
   456 
   457 struct razor_importer *
   458 razor_importer_new(void)
   459 {
   460 	struct razor_importer *importer;
   461 
   462 	importer = zalloc(sizeof *importer);
   463 	importer->set = razor_set_create();
   464 	importer->requires.all = &importer->set->requires;
   465 	importer->provides.all = &importer->set->provides;
   466 
   467 	return importer;
   468 }
   469 
   470 typedef int (*compare_with_data_func_t)(const void *p1,
   471 					const void *p,
   472 					void *data);
   473 
   474 struct qsort_context {
   475 	size_t size;
   476 	compare_with_data_func_t compare;
   477 	void *data;
   478 };
   479 
   480 static void
   481 qsort_swap(void *p1, void *p2, size_t size)
   482 {
   483 	char buffer[size];
   484 
   485 	memcpy(buffer, p1, size);
   486 	memcpy(p1, p2, size);
   487 	memcpy(p2, buffer, size);
   488 }
   489 
   490 static void
   491 __qsort_with_data(void *base, size_t nelem, unsigned long *map,
   492 		  struct qsort_context *ctx)
   493 {
   494 	void *p, *start, *end, *pivot;
   495 	unsigned long *mp, *mstart, *mend, tmp;
   496 	int left, right, result;
   497 	size_t size = ctx->size;
   498 
   499 	p = base;
   500 	start = base;
   501 	end = base + nelem * size;
   502 	mp = map;
   503 	mstart = map;
   504 	mend = map + nelem;
   505 	pivot = base + (random() % nelem) * size;
   506 
   507 	while (p < end) {
   508 		result = ctx->compare(p, pivot, ctx->data);
   509 		if (result < 0) {
   510 			qsort_swap(p, start, size);
   511 			tmp = *mp;
   512 			*mp = *mstart;
   513 			*mstart = tmp;
   514 			if (start == pivot)
   515 				pivot = p;
   516 			start += size;
   517 			mstart++;
   518 			p += size;
   519 			mp++;
   520 		} else if (result == 0) {
   521 			p += size;
   522 			mp++;
   523 		} else {
   524  			end -= size;
   525 			mend--;
   526 			qsort_swap(p, end, size);
   527 			tmp = *mp;
   528 			*mp = *mend;
   529 			*mend = tmp;
   530 			if (end == pivot)
   531 				pivot = p;
   532 		}
   533 	}
   534 
   535 	left = (start - base) / size;
   536 	right = (base + nelem * size - end) / size;
   537 	if (left > 1)
   538 		__qsort_with_data(base, left, map, ctx);
   539 	if (right > 1)
   540 		__qsort_with_data(end, right, mend, ctx);
   541 }
   542 
   543 unsigned long *
   544 qsort_with_data(void *base, size_t nelem, size_t size,
   545 		compare_with_data_func_t compare, void *data)
   546 {
   547 	struct qsort_context ctx;
   548 	unsigned long *map;
   549 	int i;
   550 
   551 	ctx.size = size;
   552 	ctx.compare = compare;
   553 	ctx.data = data;
   554 
   555 	map = malloc(nelem * sizeof (unsigned long));
   556 	for (i = 0; i < nelem; i++)
   557 		map[i] = i;
   558 
   559 	__qsort_with_data(base, nelem, map, &ctx);
   560 
   561 	return map;
   562 }
   563 
   564 static int
   565 versioncmp(const char *s1, const char *s2)
   566 {
   567 	const char *p1, *p2;
   568 	long n1, n2;
   569 	int res;
   570 
   571 	n1 = strtol(s1, (char **) &p1, 0);
   572 	n2 = strtol(s2, (char **) &p2, 0);
   573 
   574 	/* Epoch; if one but not the other has an epoch set, default
   575 	 * the epoch-less version to 0. */
   576 	res = (*p1 == ':') - (*p2 == ':');
   577 	if (res < 0) {
   578 		n1 = 0;
   579 		p1 = s1;
   580 		p2++;
   581 	} else if (res > 0) {
   582 		p1++;
   583 		n2 = 0;
   584 		p2 = s2;
   585 	}
   586 
   587 	if (n1 != n2)
   588 		return n1 - n2;
   589 	while (*p1 && *p2) {
   590 		if (*p1 != *p2)
   591 			return *p1 - *p2;
   592 		p1++;
   593 		p2++;
   594 		if (isdigit(*p1) && isdigit(*p2))
   595 			return versioncmp(p1, p2);
   596 	}
   597 
   598 	return *p1 - *p2;
   599 }
   600 
   601 
   602 static int
   603 compare_packages(const void *p1, const void *p2, void *data)
   604 {
   605 	const struct razor_package *pkg1 = p1, *pkg2 = p2;
   606 	struct razor_set *set = data;
   607 	char *pool = set->string_pool.data;
   608 
   609 	if (pkg1->name == pkg2->name)
   610 		return versioncmp(&pool[pkg1->version], &pool[pkg2->version]);
   611 	else
   612 		return strcmp(&pool[pkg1->name], &pool[pkg2->name]);
   613 }
   614 
   615 static int
   616 compare_properties(const void *p1, const void *p2, void *data)
   617 {
   618 	const struct razor_property *prop1 = p1, *prop2 = p2;
   619 	struct razor_set *set = data;
   620 	char *pool = set->string_pool.data;
   621 
   622 	if (prop1->name == prop2->name)
   623 		return versioncmp(&pool[prop1->version], &pool[prop2->version]);
   624 	else
   625 		return strcmp(&pool[prop1->name], &pool[prop2->name]);
   626 }
   627 
   628 static unsigned long *
   629 uniqueify_properties(struct razor_set *set, struct array *properties)
   630 {
   631 	struct razor_property *rp, *up, *rp_end;
   632 	struct array *pkgs, *p;
   633 	unsigned long *map, *rmap, *r;
   634 	int i, count, unique;
   635 
   636 	count = properties->size / sizeof(struct razor_property);
   637 	map = qsort_with_data(properties->data,
   638 			      count,
   639 			      sizeof(struct razor_property),
   640 			      compare_properties,
   641 			      set);
   642 
   643 	rp_end = properties->data + properties->size;
   644 	rmap = malloc(count * sizeof *map);
   645 	pkgs = zalloc(count * sizeof *pkgs);
   646 	for (rp = properties->data, up = rp, i = 0; rp < rp_end; rp++, i++) {
   647 		if (rp->name != up->name || rp->version != up->version) {
   648 			up++;
   649 			up->name = rp->name;
   650 			up->version = rp->version;
   651 		}
   652 
   653 		unique = up - (struct razor_property *) properties->data;
   654 		rmap[map[i]] = unique;
   655 		r = array_add(&pkgs[unique], sizeof *r);
   656 		*r = rp->packages;
   657 	}
   658 	free(map);
   659 
   660 	up++;
   661 	properties->size = (void *) up - properties->data;
   662 	rp_end = up;
   663 	for (rp = properties->data, p = pkgs; rp < rp_end; rp++, p++) {
   664 		rp->packages = add_to_property_pool(&set->package_pool, p);
   665 		array_release(p);
   666 	}
   667 
   668 	free(pkgs);
   669 
   670 	return rmap;
   671 }
   672 
   673 static void
   674 remap_links(struct array *links, unsigned long *map)
   675 {
   676 	unsigned long *p, *end;
   677 
   678 	end = links->data + links->size;
   679 	for (p = links->data; p < end; p++)
   680 		if (*p != ~0)
   681 			*p = map[*p];
   682 }
   683 
   684 struct razor_set *
   685 razor_importer_finish(struct razor_importer *importer)
   686 {
   687 	struct razor_set *set;
   688 	unsigned long *map, *rmap;
   689 	int i, count;
   690 
   691 	map = uniqueify_properties(importer->set, &importer->set->requires);
   692 	remap_links(&importer->set->requires_pool, map);
   693 	free(map);
   694 
   695 	map = uniqueify_properties(importer->set, &importer->set->provides);
   696 	remap_links(&importer->set->provides_pool, map);
   697 	free(map);
   698 
   699 	count = importer->set->packages.size / sizeof(struct razor_package);
   700 	map = qsort_with_data(importer->set->packages.data,
   701 			      count,
   702 			      sizeof(struct razor_package),
   703 			      compare_packages,
   704 			      importer->set);
   705 
   706 	rmap = malloc(count * sizeof *rmap);
   707 	for (i = 0; i < count; i++)
   708 		rmap[map[i]] = i;
   709 
   710 	remap_links(&importer->set->package_pool, rmap);
   711 	free(map);
   712 	free(rmap);
   713 
   714 	set = importer->set;
   715 	array_release(&importer->buckets);
   716 	free(importer);
   717 
   718 	return set;
   719 }
   720 
   721 void
   722 razor_set_list(struct razor_set *set)
   723 {
   724 	struct razor_package *p, *end;
   725 	char *pool;
   726 
   727 	pool = set->string_pool.data;
   728 	end = set->packages.data + set->packages.size;
   729 	for (p = set->packages.data; p < end; p++)
   730 		printf("%s %s\n", &pool[p->name], &pool[p->version]);
   731 }
   732 
   733 struct razor_set *bsearch_set;
   734 
   735 static int
   736 compare_package_name(const void *key, const void *data)
   737 {
   738 	const struct razor_package *p = data;
   739 	char *pool;
   740 
   741 	pool = bsearch_set->string_pool.data;
   742 
   743 	return strcmp(key, &pool[p->name]);
   744 }
   745 
   746 struct razor_package *
   747 razor_set_get_package(struct razor_set *set, const char *package)
   748 {
   749 	bsearch_set = set;
   750 	return bsearch(package, set->packages.data,
   751 		       set->packages.size / sizeof(struct razor_package),
   752 		       sizeof(struct razor_package), compare_package_name);
   753 }
   754 
   755 static int
   756 compare_property_name(const void *key, const void *data)
   757 {
   758 	const struct razor_property *p = data;
   759 	char *pool;
   760 
   761 	pool = bsearch_set->string_pool.data;
   762 
   763 	return strcmp(key, &pool[p->name]);
   764 }
   765 
   766 struct razor_property *
   767 razor_set_get_property(struct razor_set *set,
   768 		       struct array *properties,
   769 		       const char *property)
   770 {
   771 	struct razor_property *p, *start;
   772 
   773 	bsearch_set = set;
   774 	p = bsearch(property, properties->data,
   775 		    properties->size / sizeof(struct razor_property),
   776 		    sizeof(struct razor_property), compare_property_name);
   777 
   778 	start = properties->data;
   779 	while (p > start && (p - 1)->name == p->name)
   780 		p--;
   781 
   782 	return p;
   783 }
   784 
   785 static void
   786 razor_set_list_all_properties(struct razor_set *set, struct array *properties)
   787 {
   788 	struct razor_property *p, *end;
   789 	char *pool;
   790 
   791 	pool = set->string_pool.data;
   792 	end = properties->data + properties->size;
   793 	for (p = properties->data; p < end; p++)
   794 		printf("%s %s\n", &pool[p->name], &pool[p->version]);
   795 }
   796 
   797 void
   798 razor_set_list_requires(struct razor_set *set, const char *name)
   799 {
   800 	struct razor_property *p, *requires;
   801 	struct razor_package *package;
   802 	unsigned long *r;
   803 	char *pool;
   804 
   805 	if (name) {
   806 		package = razor_set_get_package(set, name);
   807 		r = (unsigned long *) set->requires_pool.data +
   808 			package->requires;
   809 		requires = set->requires.data;
   810 		pool = set->string_pool.data;
   811 		while (~*r) {
   812 			p = &requires[*r++];
   813 			printf("%s %s\n", &pool[p->name], &pool[p->version]);
   814 		}
   815 	} else
   816 		razor_set_list_all_properties(set, &set->requires);
   817 }
   818 
   819 void
   820 razor_set_list_provides(struct razor_set *set, const char *name)
   821 {
   822 	struct razor_property *p, *provides;
   823 	struct razor_package *package;
   824 	unsigned long *r;
   825 	char *pool;
   826 
   827 	if (name) {
   828 		package = razor_set_get_package(set, name);
   829 		r = (unsigned long *) set->provides_pool.data +
   830 			package->provides;
   831 		provides = set->provides.data;
   832 		pool = set->string_pool.data;
   833 		while (~*r) {
   834 			p = &provides[*r++];
   835 			printf("%s %s\n", &pool[p->name], &pool[p->version]);
   836 		}
   837 	} else 
   838 		razor_set_list_all_properties(set, &set->provides);
   839 }
   840 
   841 void
   842 razor_set_list_property_packages(struct razor_set *set,
   843 				 struct array *properties,
   844 				 const char *name,
   845 				 const char *version)
   846 {
   847 	struct razor_property *property, *end;
   848 	struct razor_package *p, *packages;
   849 	unsigned long *r;
   850 	char *pool;
   851 
   852 	if (name == NULL)
   853 		return;
   854 
   855 	property = razor_set_get_property(set, properties, name);
   856 	packages = set->packages.data;
   857 	pool = set->string_pool.data;
   858 	end = properties->data + properties->size;
   859 	while (property < end && strcmp(name, &pool[property->name]) == 0) {
   860 		if (version && versioncmp(version, &pool[property->version]) != 0)
   861 			goto next;
   862 		r = (unsigned long *)
   863 			set->package_pool.data + property->packages;
   864 		while (~*r) {
   865 			p = &packages[*r++];
   866 			printf("%s %s\n",
   867 			       &pool[p->name], &pool[p->version]);
   868 		}
   869 	next:
   870 		property++;
   871 	}
   872 }
   873 
   874 void
   875 razor_set_validate(struct razor_set *set, struct array *unsatisfied)
   876 {
   877 	struct razor_property *r, *p, *rend, *pend;
   878 	unsigned long *u;
   879 	char *pool;
   880 
   881 	p = set->provides.data;
   882 	rend = set->requires.data + set->requires.size;
   883 	pend = set->provides.data + set->provides.size;
   884 	pool = set->string_pool.data;
   885 	
   886 	for (r = set->requires.data; r < rend; r++) {
   887 		while (p < pend && strcmp(&pool[r->name], &pool[p->name]) > 0)
   888 			p++;
   889 
   890 		/* If there is more than one version of a provides,
   891 		 * seek to the end for the highest version. */
   892 		while (p + 1 < pend && p->name == (p + 1)->name)
   893 			p++;
   894 
   895 		/* FIXME: We need to track property flags (<, <=, =
   896 		 * etc) to properly determine if a requires is
   897 		 * satisfied.  The current code doesn't track that the
   898 		 * requires a = 1 isn't satisfied by a = 2 provides. */
   899 
   900 		if (p == pend || strcmp(&pool[r->name], &pool[p->name]) != 0 ||
   901 		    versioncmp(&pool[r->version], &pool[p->version]) > 0) {
   902 			/* FIXME: We ignore file requires for now. */
   903 			if (pool[r->name] == '/')
   904 				continue;
   905 			u = array_add(unsatisfied, sizeof *u);
   906 			*u = r - (struct razor_property *) set->requires.data;
   907 		}
   908 	}
   909 }
   910 
   911 void
   912 razor_set_list_unsatisfied(struct razor_set *set)
   913 {
   914 	struct array unsatisfied;
   915 	struct razor_property *requires, *r;
   916 	unsigned long *u, *end;
   917 	char *pool;
   918 
   919 	array_init(&unsatisfied);
   920 	razor_set_validate(set, &unsatisfied);
   921 
   922 	end = unsatisfied.data + unsatisfied.size;
   923 	requires = set->requires.data;
   924 	pool = set->string_pool.data;
   925 
   926 	for (u = unsatisfied.data; u < end; u++) {
   927 		r = requires + *u;
   928 		printf("%s %s not satisfied\n",
   929 		       &pool[r->name], &pool[r->version]);
   930 	}
   931 
   932 	array_release(&unsatisfied);
   933 }
   934 
   935 static void
   936 add_package(struct razor_importer *importer,
   937 	    struct razor_package *package, struct razor_set *set)
   938 {
   939 	char *pool;
   940 	unsigned long *r;
   941 	struct razor_property *p, *properties;
   942 
   943 	pool = set->string_pool.data;
   944 	razor_importer_begin_package(importer,
   945 				     &pool[package->name],
   946 				     &pool[package->version]);
   947 
   948 	r = (unsigned long *) set->requires_pool.data + package->requires;
   949 	properties = set->requires.data;
   950 	while (~*r) {
   951 		p = &properties[*r++];
   952 		razor_importer_add_requires(importer,
   953 					    &pool[p->name], &pool[p->version]);
   954 	}
   955 
   956 	r = (unsigned long *) set->provides_pool.data + package->provides;
   957 	properties = set->provides.data;
   958 	while (~*r) {
   959 		p = &properties[*r++];
   960 		razor_importer_add_provides(importer,
   961 					    &pool[p->name], &pool[p->version]);
   962 	}
   963 
   964 	razor_importer_finish_package(importer);
   965 }
   966 
   967 /* Add packages from 'upstream' to 'set'.  The packages to add are
   968  * specified by the 'packages' array, which is a sorted list of
   969  * package indexes.  Returns a newly allocated package set.  Does not
   970  * enforce validity of the resulting package set. */
   971 
   972 /* FIXME: We can do this in a linear sweep instead of using an
   973  * importer and the sorting that incurs: build the new package list
   974  * sorted, build up a map from package index in old set to package
   975  * index in new set for both sets. ~0 means 'not in new set'.  build
   976  * new string pool as we go, probably just re-use that part of the
   977  * importer.  as we build the package list, fill out a bitvector of
   978  * the properties that are referenced by the pacakges in the new
   979  * set. then do a parallel loop through the properties and emit them
   980  * to the new set and build a map from indices in the old set to
   981  * indices in the new set. then loop through the packages again and
   982  * emit the property lists. */
   983 
   984 struct razor_set *
   985 razor_set_add(struct razor_set *set, struct razor_set *upstream,
   986 	      struct array *packages)
   987 {
   988 	struct razor_importer *importer;
   989 	struct razor_package *upstream_packages, *p, *s, *send;
   990 	char *spool, *upool;
   991 	unsigned long *u, *uend;
   992 	int cmp;
   993 
   994 	importer = razor_importer_new();
   995 	upstream_packages = upstream->packages.data;
   996 	u = packages->data;
   997 	uend = packages->data + packages->size;
   998 	upool = upstream->string_pool.data;
   999 	s = set->packages.data;
  1000 	send = set->packages.data + set->packages.size;
  1001 	spool = set->string_pool.data;
  1002 
  1003 	while (s < send) {
  1004 		p = upstream_packages + *u;
  1005 		if (u < uend)
  1006 			cmp = strcmp(&spool[s->name], &upool[p->name]);
  1007 		if (u >= uend || cmp < 0) {
  1008 			add_package(importer, s, set);
  1009 			s++;
  1010 		} else if (cmp == 0) {
  1011 			add_package(importer, p, upstream);
  1012 			s++;
  1013 			u++;
  1014 		} else {
  1015 			add_package(importer, p, upstream);
  1016 			u++;
  1017 		}
  1018 	}
  1019 
  1020 	return razor_importer_finish(importer);
  1021 }
  1022 
  1023 void
  1024 razor_set_satisfy(struct razor_set *set, struct array *unsatisfied,
  1025 		  struct razor_set *upstream, struct array *list)
  1026 {
  1027 	struct razor_property *requires, *r;
  1028 	struct razor_property *p, *pend;
  1029 	unsigned long *u, *end, *pkg, *package_pool;
  1030 	char *pool, *upool;
  1031 
  1032 	end = unsatisfied->data + unsatisfied->size;
  1033 	requires = set->requires.data;
  1034 	pool = set->string_pool.data;
  1035 
  1036 	p = upstream->provides.data;
  1037 	pend = upstream->provides.data + upstream->provides.size;
  1038 	upool = upstream->string_pool.data;
  1039 	package_pool = upstream->package_pool.data;
  1040 
  1041 	for (u = unsatisfied->data; u < end; u++) {
  1042 		r = requires + *u;
  1043 
  1044 		while (p < pend && strcmp(&pool[r->name], &upool[p->name]) > 0)
  1045 			p++;
  1046 		/* If there is more than one version of a provides,
  1047 		 * seek to the end for the highest version. */
  1048 		while (p + 1 < pend && p->name == (p + 1)->name)
  1049 			p++;
  1050 
  1051 		if (p == pend ||
  1052 		    strcmp(&pool[r->name], &upool[p->name]) != 0 ||
  1053 		    versioncmp(&pool[r->version], &upool[p->version]) > 0) {
  1054 			/* Do we need to track unsatisfiable requires
  1055 			 * as we go, or should we just do a
  1056 			 * razor_set_validate() at the end? */
  1057 		} else {
  1058 			pkg = array_add(list, sizeof *pkg);
  1059 			/* We just pull in the first package that provides */
  1060 			*pkg = package_pool[p->packages];
  1061 		}
  1062 	}	
  1063 }
  1064 
  1065 static void
  1066 find_packages(struct razor_set *set,
  1067 	      int count, const char **packages, struct array *list)
  1068 {
  1069 	struct razor_package *p;
  1070 	unsigned long *r;
  1071 	int i;
  1072 
  1073 	/* FIXME: Sort the packages. */
  1074 	for (i = 0; i < count; i++) {
  1075 		p = razor_set_get_package(set, packages[i]);
  1076 		r = array_add(list, sizeof *r);
  1077 		*r = p - (struct razor_package *) set->packages.data;
  1078 	}
  1079 }
  1080 
  1081 static void
  1082 find_all_packages(struct razor_set *set,
  1083 		  struct razor_set *upstream, struct array *list)
  1084 {
  1085 	struct razor_package *p, *u, *pend, *uend;
  1086 	unsigned long *r;
  1087 	char *pool, *upool;
  1088 
  1089 	pend = set->packages.data + set->packages.size;
  1090 	pool = set->string_pool.data;
  1091 	u = upstream->packages.data;
  1092 	uend = upstream->packages.data + upstream->packages.size;
  1093 	upool = upstream->string_pool.data;
  1094 
  1095 	for (p = set->packages.data; p < pend; p++) {
  1096 		while (u < uend && strcmp(&pool[p->name], &upool[u->name]) > 0)
  1097 			u++;
  1098 		if (strcmp(&pool[p->name], &upool[u->name]) == 0) {
  1099 			r = array_add(list, sizeof *r);
  1100 			*r = u - (struct razor_package *) upstream->packages.data;
  1101 		}
  1102 	}
  1103 }
  1104 
  1105 struct razor_set *
  1106 razor_set_update(struct razor_set *set, struct razor_set *upstream,
  1107 		 int count, const char **packages)
  1108 {
  1109 	struct razor_set *new;
  1110 	struct razor_package *p, *upackages;
  1111 	struct array list, unsatisfied;
  1112 	char *pool;
  1113 	unsigned long *u, *end;
  1114 	int total = 0;
  1115 
  1116 	array_init(&list);
  1117 	if (count > 0)
  1118 		find_packages(upstream, count, packages, &list);
  1119 	else
  1120 		find_all_packages(set, upstream, &list);
  1121 
  1122 	end = list.data + list.size;
  1123 	upackages = upstream->packages.data;
  1124 	pool = upstream->string_pool.data;
  1125 	for (u = list.data; u < end; u++) {
  1126 		p = upackages + *u;
  1127 		printf("package %s-%s set to be updated\n",
  1128 		       &pool[p->name], &pool[p->version]);
  1129 	}
  1130 	total += list.size / sizeof *u;
  1131 
  1132 	while (list.size > 0) {
  1133 		printf(" -- satisfying new requires\n");
  1134 
  1135 		new = razor_set_add(set, upstream, &list);
  1136 		array_release(&list);
  1137 		razor_set_destroy(set);
  1138 		set = new;
  1139 
  1140 		array_init(&unsatisfied);
  1141 		razor_set_validate(new, &unsatisfied);
  1142 		array_init(&list);
  1143 		razor_set_satisfy(new, &unsatisfied, upstream, &list);
  1144 		array_release(&unsatisfied);
  1145 
  1146 		end = list.data + list.size;
  1147 		upackages = upstream->packages.data;
  1148 		pool = upstream->string_pool.data;
  1149 		for (u = list.data; u < end; u++) {
  1150 			p = upackages + *u;
  1151 			printf("package %s-%s set to be updated\n",
  1152 			       &pool[p->name], &pool[p->version]);
  1153 		}
  1154 		total += list.size / sizeof *u;
  1155 	}
  1156 
  1157 	array_release(&list);
  1158 
  1159 	printf("total of %d packages set to be updated\n", total);
  1160 
  1161 	return set;
  1162 }
  1163 
  1164 void
  1165 razor_set_info(struct razor_set *set)
  1166 {
  1167 	unsigned int offset, size;
  1168 	int i;
  1169 
  1170 	for (i = 0; i < set->header->sections[i].type; i++) {
  1171 		offset = set->header->sections[i].offset;
  1172 		size = set->header->sections[i].size;
  1173 
  1174 		switch (set->header->sections[i].type) {
  1175 		case RAZOR_PACKAGES:
  1176 			printf("package section:\t%dkb\n", size / 1024);
  1177 			break;
  1178 		case RAZOR_REQUIRES:
  1179 			printf("requires section:\t%dkb\n", size / 1024);
  1180 			break;
  1181 		case RAZOR_PROVIDES:
  1182 			printf("provides section:\t%dkb\n", size / 1024);
  1183 			break;
  1184 		case RAZOR_STRING_POOL:
  1185 			printf("string pool:\t\t%dkb\n", size / 1024);
  1186 			break;
  1187 		}
  1188 	}
  1189 }
  1190 
  1191 static int
  1192 usage(void)
  1193 {
  1194 	printf("usage: razor [ import FILES | lookup <key> | "
  1195 	       "list | list-requires | list-provides | eat-yum | info ]\n");
  1196 	exit(1);
  1197 }
  1198 
  1199 static const char *repo_filename = "system.repo";
  1200 static const char rawhide_repo_filename[] = "rawhide.repo";
  1201 
  1202 int
  1203 main(int argc, const char *argv[])
  1204 {
  1205 	struct razor_set *set, *upstream;
  1206 	struct stat statbuf;
  1207 	char *repo;
  1208 
  1209 	repo = getenv("RAZOR_REPO");
  1210 	if (repo != NULL)
  1211 		repo_filename = repo;
  1212 
  1213 	if (argc < 2) {
  1214 		usage();
  1215 	} else if (strcmp(argv[1], "import") == 0) {
  1216 		if (stat("set", &statbuf) && mkdir("set", 0777)) {
  1217 			fprintf(stderr, "could not create directory 'set'\n");
  1218 			exit(-1);
  1219 		}
  1220 			
  1221 		set = razor_import_rzr_files(argc - 2, argv + 2);
  1222 
  1223 		printf("pool size: %d\n", set->string_pool.size);
  1224 		printf("pool allocation: %d\n", set->string_pool.alloc);
  1225 		printf("packages: %d\n",
  1226 		       set->packages.size / sizeof(struct razor_package));
  1227 		printf("requires: %d\n",
  1228 		       set->requires.size / sizeof(struct razor_property));
  1229 		printf("provides: %d\n",
  1230 		       set->provides.size / sizeof(struct razor_property));
  1231 
  1232 		razor_set_write(set, repo_filename);
  1233 
  1234 		razor_set_destroy(set);
  1235 	} else if (strcmp(argv[1], "list") == 0) {
  1236 		set = razor_set_open(repo_filename);
  1237 		razor_set_list(set);
  1238 		razor_set_destroy(set);
  1239 	} else if (strcmp(argv[1], "list-requires") == 0) {
  1240 		set = razor_set_open(repo_filename);
  1241 		razor_set_list_requires(set, argv[2]);
  1242 		razor_set_destroy(set);
  1243 	} else if (strcmp(argv[1], "list-provides") == 0) {
  1244 		set = razor_set_open(repo_filename);
  1245 		razor_set_list_provides(set, argv[2]);
  1246 		razor_set_destroy(set);
  1247 	} else if (strcmp(argv[1], "what-requires") == 0) {
  1248 		set = razor_set_open(repo_filename);
  1249 		razor_set_list_property_packages(set, &set->requires,
  1250 						 argv[2], argv[3]);
  1251 		razor_set_destroy(set);
  1252 	} else if (strcmp(argv[1], "what-provides") == 0) {
  1253 		set = razor_set_open(repo_filename);
  1254 		razor_set_list_property_packages(set, &set->provides,
  1255 						 argv[2], argv[3]);
  1256 		razor_set_destroy(set);
  1257 	} else if (strcmp(argv[1], "info") == 0) {
  1258 		set = razor_set_open(repo_filename);
  1259 		razor_set_info(set);
  1260 		razor_set_destroy(set);
  1261 	} else if (strcmp(argv[1], "eat-yum") == 0) {
  1262 		set = razor_set_create_from_yum_filelist(STDIN_FILENO);
  1263 		if (set == NULL)
  1264 			return 1;
  1265 		razor_set_write(set, rawhide_repo_filename);
  1266 		razor_set_destroy(set);
  1267 		printf("wrote %s\n", rawhide_repo_filename);
  1268 	} else if (strcmp(argv[1], "import-rpmdb") == 0) {
  1269 		set = razor_set_create_from_rpmdb();
  1270 		if (set == NULL)
  1271 			return 1;
  1272 		razor_set_write(set, repo_filename);
  1273 		razor_set_destroy(set);
  1274 		printf("wrote %s\n", repo_filename);
  1275 	} else if (strcmp(argv[1], "validate") == 0) {
  1276 		set = razor_set_open(repo_filename);
  1277 		if (set == NULL)
  1278 			return 1;
  1279 		razor_set_list_unsatisfied(set);
  1280 		razor_set_destroy(set);
  1281 	} else if (strcmp(argv[1], "update") == 0) {
  1282 		set = razor_set_open(repo_filename);
  1283 		upstream = razor_set_open(rawhide_repo_filename);
  1284 		if (set == NULL || upstream == NULL)
  1285 			return 1;
  1286 		set = razor_set_update(set, upstream, argc - 2, argv + 2);
  1287 		razor_set_write(set, "system-updated.repo");
  1288 		razor_set_destroy(set);
  1289 		razor_set_destroy(upstream);
  1290 		printf("wrote system-updated.repo\n");
  1291 	} else {
  1292 		usage();
  1293 	}
  1294 
  1295 	return 0;
  1296 }