librazor/importer.c
author James Bowes <jbowes@redhat.com>
Wed Jul 09 10:11:13 2008 -0400 (2008-07-09)
changeset 318 829d6711b316
parent 269 03fc85294bc9
child 359 c9c90315ea24
permissions -rw-r--r--
Use strings to identify section types in the on-disk repo format.

Previously, a given razor file type had a fixed number of sections in a
fixed order, identified by an integer type. Now, sections are identified
by a named string (stored in a string pool after the section lists).

This will allow for razor files to contain arbitrary sections.

For bonus points, also drop the 4k section alignment and change the
magic byte string to "RZDB".

committer: Kristian H?gsberg <krh@redhat.com>
     1 /*
     2  * Copyright (C) 2008  Kristian Høgsberg <krh@redhat.com>
     3  * Copyright (C) 2008  Red Hat, Inc
     4  *
     5  * This program is free software; you can redistribute it and/or modify
     6  * it under the terms of the GNU General Public License as published by
     7  * the Free Software Foundation; either version 2 of the License, or
     8  * (at your option) any later version.
     9  *
    10  * This program is distributed in the hope that it will be useful,
    11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13  * GNU General Public License for more details.
    14  *
    15  * You should have received a copy of the GNU General Public License along
    16  * with this program; if not, write to the Free Software Foundation, Inc.,
    17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
    18  */
    19 
    20 #define _GNU_SOURCE
    21 
    22 #include <string.h>
    23 #include "razor-internal.h"
    24 #include "razor.h"
    25 
    26 /**
    27  * razor_importer_create:
    28  *
    29  * Create a new %razor_importer.
    30  *
    31  * Returns: the new %razor_importer.
    32  **/
    33 RAZOR_EXPORT struct razor_importer *
    34 razor_importer_create(void)
    35 {
    36 	struct razor_importer *importer;
    37 
    38 	importer = zalloc(sizeof *importer);
    39 	importer->set = razor_set_create();
    40 	hashtable_init(&importer->table, &importer->set->string_pool);
    41 	hashtable_init(&importer->details_table,
    42 		       &importer->set->details_string_pool);
    43 	hashtable_init(&importer->file_table,
    44 		       &importer->set->file_string_pool);
    45 
    46 	return importer;
    47 }
    48 
    49 /**
    50  * razor_importer_destroy:
    51  * @importer: the %razor_importer
    52  *
    53  * Destroy an importer without creating a %razor_set.  Normally,
    54  * %razor_importer_finish will create a new %razor_set and destroy the
    55  * importer.  If the import must be aborted without creating the set,
    56  * just destroy the import using this function.
    57  **/
    58 RAZOR_EXPORT void
    59 razor_importer_destroy(struct razor_importer *importer)
    60 {
    61 	/* FIXME: write this */
    62 }
    63 
    64 
    65 /**
    66  * razor_importer_begin_package:
    67  * @importer: the %razor_importer
    68  * @name: the name of the new package
    69  * @version: the version of the new package
    70  * @arch: the architechture of the new package.
    71  *
    72  * Begin describing a new package to the importer.  This creates a new
    73  * package and sets the %name, %version and %arch.  Subsequent calls
    74  * to %razor_importer_add_details, %razor_importer_add_property and
    75  * %razor_importer_add_file further describe this package and
    76  * %razor_importer_finish_package marks the end of meta data for this
    77  * package.
    78  **/
    79 RAZOR_EXPORT void
    80 razor_importer_begin_package(struct razor_importer *importer,
    81 			     const char *name,
    82 			     const char *version,
    83 			     const char *arch)
    84 {
    85 	struct razor_package *p;
    86 
    87 	p = array_add(&importer->set->packages, sizeof *p);
    88 	p->name = hashtable_tokenize(&importer->table, name);
    89 	p->flags = 0;
    90 	p->version = hashtable_tokenize(&importer->table, version);
    91 	p->arch = hashtable_tokenize(&importer->table, arch);
    92 
    93 	importer->package = p;
    94 	array_init(&importer->properties);
    95 }
    96 
    97 /**
    98  * razor_importer_finish_package:
    99  * @importer: the %razor_importer
   100  *
   101  * Tells the importer that the current package is complete.
   102  **/
   103 RAZOR_EXPORT void
   104 razor_importer_finish_package(struct razor_importer *importer)
   105 {
   106 	list_set_array(&importer->package->properties,
   107 		       &importer->set->property_pool,
   108 		       &importer->properties,
   109 		       1);
   110 
   111 	array_release(&importer->properties);
   112 }
   113 
   114 /**
   115  * razor_importer_add_details:
   116  * @importer: the %razor_importer
   117  * @summary: the package summary
   118  * @description: the package description
   119  * @url: the package url
   120  * @license: the package license
   121  *
   122  * Provide additional information for the current package.
   123  **/
   124 RAZOR_EXPORT void
   125 razor_importer_add_details(struct razor_importer *importer,
   126 			   const char *summary,
   127 			   const char *description,
   128 			   const char *url,
   129 			   const char *license)
   130 {
   131 	importer->package->summary = hashtable_tokenize(&importer->details_table, summary);
   132 	importer->package->description = hashtable_tokenize(&importer->details_table, description);
   133 	importer->package->url = hashtable_tokenize(&importer->details_table, url);
   134 	importer->package->license = hashtable_tokenize(&importer->details_table, license);
   135 }
   136 
   137 /**
   138  * razor_importer_add_property:
   139  * @importer: the %razor_importer
   140  * @name: name of the property
   141  * @flags: property flags
   142  * @version: version of the property or %NULL
   143  *
   144  * Add a property for the current package.  The %flags parameter
   145  * determines the type of the property and optionally the relation to
   146  * the specified version and the availability constraint.  See
   147  * %razor_property_flags for further information about the flag
   148  * parameter.
   149  **/
   150 RAZOR_EXPORT void
   151 razor_importer_add_property(struct razor_importer *importer,
   152 			    const char *name,
   153 			    uint32_t flags,
   154 			    const char *version)
   155 {
   156 	struct razor_property *p;
   157 	uint32_t *r;
   158 
   159 	p = array_add(&importer->set->properties, sizeof *p);
   160 	p->name = hashtable_tokenize(&importer->table, name);
   161 	p->flags = flags;
   162 	p->version = hashtable_tokenize(&importer->table, version);
   163 	list_set_ptr(&p->packages, importer->package -
   164 		     (struct razor_package *) importer->set->packages.data);
   165 
   166 	r = array_add(&importer->properties, sizeof *r);
   167 	*r = p - (struct razor_property *) importer->set->properties.data;
   168 
   169 	if (((flags & RAZOR_PROPERTY_TYPE_MASK) == RAZOR_PROPERTY_REQUIRES) &&
   170 	    *name == '/') {
   171 		r = array_add(&importer->file_requires, sizeof *r);
   172 		*r = p->name;
   173 	}
   174 }
   175 
   176 /**
   177  * razor_importer_add_file:
   178  * @importer: the %razor_importer
   179  * @name: name of the file
   180  *
   181  * Add a file to the current package.
   182  **/
   183 RAZOR_EXPORT void
   184 razor_importer_add_file(struct razor_importer *importer, const char *name)
   185 {
   186 	struct import_entry *e;
   187 
   188 	e = array_add(&importer->files, sizeof *e);
   189 
   190 	e->package = importer->package -
   191 		(struct razor_package *) importer->set->packages.data;
   192 	e->name = strdup(name);
   193 }
   194 
   195 static int
   196 compare_packages(const void *p1, const void *p2, void *data)
   197 {
   198 	const struct razor_package *pkg1 = p1, *pkg2 = p2;
   199 	struct razor_set *set = data;
   200 	char *pool = set->string_pool.data;
   201 
   202 	/* FIXME: what if the flags are different? */
   203 	if (pkg1->name == pkg2->name)
   204 		return razor_versioncmp(&pool[pkg1->version], &pool[pkg2->version]);
   205 	else
   206 		return strcmp(&pool[pkg1->name], &pool[pkg2->name]);
   207 }
   208 
   209 static int
   210 compare_properties(const void *p1, const void *p2, void *data)
   211 {
   212 	const struct razor_property *prop1 = p1, *prop2 = p2;
   213 	struct razor_set *set = data;
   214 	char *pool = set->string_pool.data;
   215 
   216 	if (prop1->name != prop2->name)
   217 		return strcmp(&pool[prop1->name], &pool[prop2->name]);
   218 	else if (prop1->flags != prop2->flags)
   219 		return prop1->flags - prop2->flags;
   220 	else if (prop1->version != prop2->version)
   221 		return razor_versioncmp(&pool[prop1->version], &pool[prop2->version]);
   222 	else
   223 		return prop1->packages.list_ptr - prop2->packages.list_ptr;
   224 }
   225 
   226 static uint32_t *
   227 uniqueify_properties(struct razor_set *set)
   228 {
   229 	struct razor_property *rp, *up, *rp_end;
   230 	struct array *pkgs, *p;
   231 	struct list_head *r;
   232 	uint32_t *map, *rmap;
   233 	int i, count, unique;
   234 
   235 	count = set->properties.size / sizeof(struct razor_property);
   236 	map = razor_qsort_with_data(set->properties.data,
   237 				    count,
   238 				    sizeof(struct razor_property),
   239 				    compare_properties,
   240 				    set);
   241 
   242 	rp_end = set->properties.data + set->properties.size;
   243 	rmap = malloc(count * sizeof *map);
   244 	pkgs = zalloc(count * sizeof *pkgs);
   245 	for (rp = set->properties.data, up = rp, i = 0; rp < rp_end; rp++, i++) {
   246 		if (rp->name != up->name ||
   247 		    rp->flags != up->flags ||
   248 		    rp->version != up->version) {
   249 			up++;
   250 			up->name = rp->name;
   251 			up->flags = rp->flags;
   252 			up->version = rp->version;
   253 		}
   254 
   255 		unique = up - (struct razor_property *) set->properties.data;
   256 		rmap[map[i]] = unique;
   257 		r = array_add(&pkgs[unique], sizeof *r);
   258 		*r = rp->packages;
   259 	}
   260 	free(map);
   261 
   262 	if (up != rp)
   263 		up++;
   264 	set->properties.size = (void *) up - set->properties.data;
   265 	rp_end = up;
   266 	for (rp = set->properties.data, p = pkgs; rp < rp_end; rp++, p++) {
   267 		list_set_array(&rp->packages, &set->package_pool, p, 0);
   268 		array_release(p);
   269 	}
   270 
   271 	free(pkgs);
   272 
   273 	return rmap;
   274 }
   275 
   276 static int
   277 compare_filenames(const void *p1, const void *p2, void *data)
   278 {
   279 	const struct import_entry *e1 = p1;
   280 	const struct import_entry *e2 = p2;
   281 	const char *n1 = e1->name;
   282 	const char *n2 = e2->name;
   283 
   284 	/* Need to make sure that the contents of a directory
   285 	 * are sorted immediately after it. So "foo/bar" has to
   286 	 * sort before "foo.conf"
   287 	 *
   288 	 * FIXME: this is about 60% slower than strcmp
   289 	 */
   290 	while (*n1 && *n2) {
   291 		if (*n1 < *n2)
   292 			return *n2 == '/' ? 1 : -1;
   293 		else if (*n1 > *n2)
   294 			return *n1 == '/' ? -1 : 1;
   295 		n1++;
   296 		n2++;
   297 	}
   298 	if (*n1)
   299 		return 1;
   300 	else if (*n2)
   301 		return -1;
   302 	else
   303 		return 0;
   304 }
   305 
   306 static void
   307 count_entries(struct import_directory *d)
   308 {
   309 	struct import_directory *p, *end;
   310 
   311 	p = d->files.data;
   312 	end = d->files.data + d->files.size;
   313 	d->count = 0;
   314 	while (p < end) {
   315 		count_entries(p);
   316 		d->count += p->count + 1;
   317 		p++;
   318 	}
   319 }
   320 
   321 static void
   322 serialize_files(struct razor_set *set,
   323 		struct import_directory *d, struct array *array)
   324 {
   325 	struct import_directory *p, *end;
   326 	struct razor_entry *e = NULL;
   327 	uint32_t s;
   328 
   329 	p = d->files.data;
   330 	end = d->files.data + d->files.size;
   331 	s = array->size / sizeof *e + d->files.size / sizeof *p;
   332 	while (p < end) {
   333 		e = array_add(array, sizeof *e);
   334 		e->name = p->name;
   335 		e->flags = 0;
   336 		e->start = p->count > 0 ? s : 0;
   337 		s += p->count;
   338 
   339 		list_set_array(&e->packages, &set->package_pool, &p->packages, 0);
   340 		array_release(&p->packages);
   341 		p++;
   342 	}
   343 	if (e != NULL)
   344 		e->flags |= RAZOR_ENTRY_LAST;
   345 
   346 	p = d->files.data;
   347 	end = d->files.data + d->files.size;
   348 	while (p < end) {
   349 		serialize_files(set, p, array);
   350 		p++;
   351 	}
   352 }
   353 
   354 static void
   355 remap_property_package_links(struct array *properties, uint32_t *rmap)
   356 {
   357 	struct razor_property *p, *end;
   358 
   359 	end = properties->data + properties->size;
   360 	for (p = properties->data; p < end; p++)
   361 		list_remap_head(&p->packages, rmap);
   362 }
   363 
   364 static void
   365 build_file_tree(struct razor_importer *importer)
   366 {
   367 	int count, i, length;
   368 	struct import_entry *filenames;
   369 	char *f, *end;
   370 	uint32_t name, *r;
   371 	char dirname[256];
   372 	struct import_directory *d, root;
   373 	struct razor_entry *e;
   374 
   375 	count = importer->files.size / sizeof (struct import_entry);
   376 	razor_qsort_with_data(importer->files.data,
   377 			      count,
   378 			      sizeof (struct import_entry),
   379 			      compare_filenames,
   380 			      NULL);
   381 
   382 	root.name = hashtable_tokenize(&importer->file_table, "");
   383 	array_init(&root.files);
   384 	array_init(&root.packages);
   385 	root.last = NULL;
   386 
   387 	filenames = importer->files.data;
   388 	for (i = 0; i < count; i++) {
   389 		f = filenames[i].name;
   390 		if (*f != '/')
   391 			continue;
   392 		f++;
   393 
   394 		d = &root;
   395 		while (*f) {
   396 			end = strchr(f, '/');
   397 			if (end == NULL)
   398 				end = f + strlen(f);
   399 			length = end - f;
   400 			memcpy(dirname, f, length);
   401 			dirname[length] ='\0';
   402 			name = hashtable_tokenize(&importer->file_table,
   403 						  dirname);
   404 			if (d->last == NULL || d->last->name != name) {
   405 				d->last = array_add(&d->files, sizeof *d);
   406 				d->last->name = name;
   407 				d->last->last = NULL;
   408 				array_init(&d->last->files);
   409 				array_init(&d->last->packages);
   410 			}
   411 			d = d->last;
   412 			f = end + 1;
   413 			if (*end == '\0')
   414 				break;
   415 		}
   416 
   417 		r = array_add(&d->packages, sizeof *r);
   418 		*r = filenames[i].package;
   419 		free(filenames[i].name);
   420 	}
   421 
   422 	count_entries(&root);
   423 	e = importer->set->files.data;
   424 	e->name = root.name;
   425 	e->flags = RAZOR_ENTRY_LAST;
   426 	e->start = importer->files.size ? 1 : 0;
   427 	list_set_empty(&e->packages);
   428 
   429 	serialize_files(importer->set, &root, &importer->set->files);
   430 
   431 	array_release(&importer->files);
   432 }
   433 
   434 static void
   435 list_to_array(struct list *list, struct array *array)
   436 {
   437 	uint32_t *item;
   438 
   439 	while (list) {
   440 		 item = array_add(array, sizeof *item);
   441 		 *item = list->data;
   442 		 list = list_next(list);
   443 	}
   444 }
   445 
   446 static int
   447 compare_file_requires(const void *p1, const void *p2, void *data)
   448 {
   449 	uint32_t *f1 = (void *)p1, *f2 = (void *)p2;
   450 	const char *pool = data;
   451 
   452 	return strcmp(&pool[*f1], &pool[*f2]);
   453 }
   454 
   455 static void
   456 find_file_provides(struct razor_importer *importer)
   457 {
   458 	struct razor_property *prop;
   459 	struct razor_entry *top, *entry;
   460 	struct razor_package *packages;
   461 	struct array pkgprops;
   462 	struct list *pkg;
   463 	uint32_t *req, *req_start, *req_end;
   464 	uint32_t *map, *newprop;
   465 	char *pool;
   466 
   467 	pool = importer->set->string_pool.data;
   468 	packages = importer->set->packages.data;
   469 	top = importer->set->files.data;
   470 
   471 	req = req_start = importer->file_requires.data;
   472 	req_end = importer->file_requires.data + importer->file_requires.size;
   473 	map = razor_qsort_with_data(req, req_end - req, sizeof *req,
   474 				    compare_file_requires, pool);
   475 	free(map);
   476 
   477 	for (req = req_start; req < req_end; req++) {
   478 		if (req > req_start && req[0] == req[-1])
   479 			continue;
   480 		entry = razor_set_find_entry(importer->set, top, &pool[*req]);
   481 		if (!entry)
   482 			continue;
   483 
   484 		for (pkg = list_first(&entry->packages, &importer->set->package_pool); pkg; pkg = list_next(pkg)) {
   485 			prop = array_add(&importer->set->properties, sizeof *prop);
   486 			prop->name = *req;
   487 			prop->flags =
   488 				RAZOR_PROPERTY_PROVIDES | RAZOR_PROPERTY_EQUAL;
   489 			prop->version = hashtable_tokenize(&importer->table, "");
   490 			list_set_ptr(&prop->packages, pkg->data);
   491 
   492 			/* Update property list of pkg */
   493 			array_init(&pkgprops);
   494 			list_to_array(list_first(&packages[pkg->data].properties, &importer->set->property_pool), &pkgprops);
   495 			newprop = array_add(&pkgprops, sizeof *newprop);
   496 			*newprop = prop - (struct razor_property *)importer->set->properties.data;
   497 			list_set_array(&packages[pkg->data].properties, &importer->set->property_pool, &pkgprops, 1);
   498 			array_release(&pkgprops);
   499 		}
   500 	}
   501 
   502 	array_release(&importer->file_requires);
   503 }
   504 
   505 static void
   506 build_package_file_lists(struct razor_set *set, uint32_t *rmap)
   507 {
   508 	struct razor_package *p, *packages;
   509 	struct array *pkgs;
   510 	struct razor_entry *e, *end;
   511 	struct list *r;
   512 	uint32_t *q;
   513 	int i, count;
   514 
   515 	count = set->packages.size / sizeof *p;
   516 	pkgs = zalloc(count * sizeof *pkgs);
   517 
   518 	end = set->files.data + set->files.size;
   519 	for (e = set->files.data; e < end; e++) {
   520 		list_remap_head(&e->packages, rmap);
   521 		r = list_first(&e->packages, &set->package_pool);
   522 		while (r) {
   523 			q = array_add(&pkgs[r->data], sizeof *q);
   524 			*q = e - (struct razor_entry *) set->files.data;
   525 			r = list_next(r);
   526 		}
   527 	}
   528 
   529 	packages = set->packages.data;
   530 	for (i = 0; i < count; i++) {
   531 		list_set_array(&packages[i].files, &set->file_pool, &pkgs[i], 0);
   532 		array_release(&pkgs[i]);
   533 	}
   534 	free(pkgs);
   535 }
   536 
   537 /**
   538  * razor_importer_finish:
   539  * @importer: the %razor_importer
   540  *
   541  * Finish importing packages and create the package set.  This sorts
   542  * and indexes all the packages, properties and files in the importer
   543  * and creates a new %razor_set.  After creating the new package set,
   544  * the importer is destroyed.
   545  *
   546  * Returns: the new %razor_set
   547  **/
   548 RAZOR_EXPORT struct razor_set *
   549 razor_importer_finish(struct razor_importer *importer)
   550 {
   551 	struct razor_set *set;
   552 	uint32_t *map, *rmap;
   553 	int i, count;
   554 
   555 	build_file_tree(importer);
   556 	find_file_provides(importer);
   557 
   558 	map = uniqueify_properties(importer->set);
   559 	list_remap_pool(&importer->set->property_pool, map);
   560 	free(map);
   561 
   562 	count = importer->set->packages.size / sizeof(struct razor_package);
   563 	map = razor_qsort_with_data(importer->set->packages.data,
   564 				    count,
   565 				    sizeof(struct razor_package),
   566 				    compare_packages,
   567 				    importer->set);
   568 
   569 	rmap = malloc(count * sizeof *rmap);
   570 	for (i = 0; i < count; i++)
   571 		rmap[map[i]] = i;
   572 	free(map);
   573 
   574 	list_remap_pool(&importer->set->package_pool, rmap);
   575 	build_package_file_lists(importer->set, rmap);
   576 	remap_property_package_links(&importer->set->properties, rmap);
   577 	free(rmap);
   578 
   579 	set = importer->set;
   580 	hashtable_release(&importer->table);
   581 	hashtable_release(&importer->details_table);
   582 	hashtable_release(&importer->file_table);
   583 	free(importer);
   584 
   585 	return set;
   586 }