krh@15: #define _GNU_SOURCE krh@15: krh@0: #include krh@19: #include krh@0: #include krh@0: #include krh@0: #include krh@0: #include krh@0: #include krh@0: #include krh@0: #include krh@15: #include krh@35: #include krh@54: #include krh@0: krh@27: #include "razor.h" krh@91: #include "razor-internal.h" krh@6: krh@30: struct array { krh@30: void *data; krh@30: int size, alloc; krh@30: }; krh@30: krh@30: struct razor_set_section { krh@30: unsigned int type; krh@30: unsigned int offset; krh@30: unsigned int size; krh@30: }; krh@30: krh@30: struct razor_set_header { krh@30: unsigned int magic; krh@30: unsigned int version; krh@30: struct razor_set_section sections[0]; krh@30: }; krh@30: krh@30: #define RAZOR_MAGIC 0x7a7a7a7a krh@30: #define RAZOR_VERSION 1 krh@30: krh@61: #define RAZOR_ENTRY_LAST 0x80000000ul krh@61: #define RAZOR_IMMEDIATE 0x80000000ul krh@61: #define RAZOR_ENTRY_MASK 0x00fffffful krh@61: krh@66: #define RAZOR_STRING_POOL 0 krh@66: #define RAZOR_PACKAGES 1 krh@66: #define RAZOR_PROPERTIES 2 krh@66: #define RAZOR_FILES 3 krh@66: #define RAZOR_PACKAGE_POOL 4 krh@66: #define RAZOR_PROPERTY_POOL 5 krh@66: #define RAZOR_FILE_POOL 6 krh@30: krh@30: struct razor_package { krh@30: unsigned long name; krh@30: unsigned long version; krh@66: unsigned long properties; krh@56: unsigned long files; krh@30: }; krh@30: krh@30: struct razor_property { krh@30: unsigned long name; krh@30: unsigned long version; krh@30: unsigned long packages; krh@30: }; krh@30: krh@48: struct razor_entry { krh@48: unsigned long name; krh@48: unsigned long start; krh@52: unsigned long packages; krh@48: }; krh@48: krh@30: struct razor_set { krh@30: struct array string_pool; krh@30: struct array packages; krh@66: struct array properties; krh@56: struct array files; krh@56: struct array package_pool; krh@66: struct array property_pool; krh@56: struct array file_pool; krh@30: struct razor_set_header *header; krh@30: }; krh@30: krh@52: struct import_entry { krh@52: unsigned long package; krh@52: char *name; krh@52: }; krh@52: krh@52: struct import_directory { krh@52: unsigned long name, count; krh@52: struct array files; krh@52: struct array packages; krh@52: struct import_directory *last; krh@52: }; krh@52: krh@78: struct hashtable { krh@78: struct array buckets; krh@78: struct array *pool; krh@78: }; krh@78: krh@30: struct razor_importer { krh@30: struct razor_set *set; krh@78: struct hashtable table; krh@30: struct razor_package *package; krh@66: struct array properties; krh@48: struct array files; krh@30: }; krh@30: krh@13: static void krh@13: array_init(struct array *array) krh@13: { krh@13: memset(array, 0, sizeof *array); krh@13: } krh@13: krh@13: static void krh@13: array_release(struct array *array) krh@13: { krh@13: free(array->data); krh@13: } krh@13: krh@6: static void * krh@6: array_add(struct array *array, int size) krh@6: { krh@6: int alloc; krh@6: void *data, *p; krh@6: krh@6: if (array->alloc > 0) krh@6: alloc = array->alloc; krh@6: else krh@10: alloc = 16; krh@6: krh@6: while (alloc < array->size + size) krh@6: alloc *= 2; krh@6: krh@6: if (array->alloc < alloc) { krh@6: data = realloc(array->data, alloc); krh@6: if (data == NULL) krh@6: return 0; krh@6: array->data = data; krh@6: array->alloc = alloc; krh@6: } krh@6: krh@6: p = array->data + array->size; krh@6: array->size += size; krh@6: krh@6: return p; krh@6: } krh@6: krh@0: static void * krh@0: zalloc(size_t size) krh@0: { krh@0: void *p; krh@0: krh@0: p = malloc(size); krh@0: memset(p, 0, size); krh@0: krh@0: return p; krh@0: } krh@0: krh@19: struct razor_set_section razor_sections[] = { krh@56: { RAZOR_STRING_POOL, offsetof(struct razor_set, string_pool) }, krh@19: { RAZOR_PACKAGES, offsetof(struct razor_set, packages) }, krh@66: { RAZOR_PROPERTIES, offsetof(struct razor_set, properties) }, krh@56: { RAZOR_FILES, offsetof(struct razor_set, files) }, krh@39: { RAZOR_PACKAGE_POOL, offsetof(struct razor_set, package_pool) }, krh@66: { RAZOR_PROPERTY_POOL, offsetof(struct razor_set, property_pool) }, krh@56: { RAZOR_FILE_POOL, offsetof(struct razor_set, file_pool) }, krh@19: }; krh@19: krh@4: struct razor_set * krh@4: razor_set_create(void) krh@0: { krh@18: return zalloc(sizeof(struct razor_set)); krh@0: } krh@0: krh@4: struct razor_set * krh@4: razor_set_open(const char *filename) krh@0: { krh@4: struct razor_set *set; krh@19: struct razor_set_section *s; krh@0: struct stat stat; krh@19: struct array *array; krh@19: int fd; krh@0: krh@4: set = zalloc(sizeof *set); krh@0: fd = open(filename, O_RDONLY); krh@0: if (fstat(fd, &stat) < 0) krh@0: return NULL; krh@4: set->header = mmap(NULL, stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0); krh@4: if (set->header == MAP_FAILED) { krh@4: free(set); krh@0: return NULL; krh@0: } krh@0: krh@19: for (s = set->header->sections; ~s->type; s++) { krh@19: if (s->type >= ARRAY_SIZE(razor_sections)) krh@19: continue; krh@19: if (s->type != razor_sections[s->type].type) krh@19: continue; krh@19: array = (void *) set + razor_sections[s->type].offset; krh@19: array->data = (void *) set->header + s->offset; krh@19: array->size = s->size; krh@19: array->alloc = s->size; krh@0: } krh@0: close(fd); krh@0: krh@4: return set; krh@0: } krh@0: krh@0: void krh@4: razor_set_destroy(struct razor_set *set) krh@0: { krh@0: unsigned int size; krh@19: struct array *a; krh@0: int i; krh@0: krh@4: if (set->header) { krh@4: for (i = 0; set->header->sections[i].type; i++) krh@0: ; krh@4: size = set->header->sections[i].type; krh@4: munmap(set->header, size); krh@0: } else { krh@19: for (i = 0; i < ARRAY_SIZE(razor_sections); i++) { krh@19: a = (void *) set + razor_sections[i].offset; krh@19: free(a->data); krh@19: } krh@0: } krh@0: krh@4: free(set); krh@0: } krh@0: krh@43: int krh@4: razor_set_write(struct razor_set *set, const char *filename) krh@0: { krh@0: char data[4096]; krh@4: struct razor_set_header *header = (struct razor_set_header *) data; krh@19: struct array *a; krh@17: unsigned long offset; krh@17: int i, fd; krh@0: krh@0: memset(data, 0, sizeof data); krh@4: header->magic = RAZOR_MAGIC; krh@4: header->version = RAZOR_VERSION; krh@17: offset = sizeof data; krh@0: krh@19: for (i = 0; i < ARRAY_SIZE(razor_sections); i++) { krh@19: if (razor_sections[i].type != i) krh@19: continue; krh@19: a = (void *) set + razor_sections[i].offset; krh@19: header->sections[i].type = i; krh@17: header->sections[i].offset = offset; krh@19: header->sections[i].size = a->size; krh@91: offset += ALIGN(a->size, 4096); krh@17: } krh@0: krh@19: header->sections[i].type = ~0; krh@17: header->sections[i].offset = 0; krh@17: header->sections[i].size = 0; krh@10: krh@0: fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0666); krh@0: if (fd < 0) krh@0: return -1; krh@0: krh@91: razor_write(fd, data, sizeof data); krh@19: for (i = 0; i < ARRAY_SIZE(razor_sections); i++) { krh@19: if (razor_sections[i].type != i) krh@19: continue; krh@19: a = (void *) set + razor_sections[i].offset; krh@91: razor_write(fd, a->data, ALIGN(a->size, 4096)); krh@19: } krh@17: krh@17: close(fd); krh@0: krh@0: return 0; krh@0: } krh@0: krh@0: static unsigned int krh@0: hash_string(const char *key) krh@0: { krh@0: const char *p; krh@0: unsigned int hash = 0; krh@0: krh@0: for (p = key; *p; p++) krh@9: hash = (hash * 617) ^ *p; krh@0: krh@0: return hash; krh@0: } krh@0: krh@32: static unsigned long krh@78: hashtable_lookup(struct hashtable *table, const char *key) krh@0: { krh@6: unsigned int mask, start, i; krh@6: unsigned long *b; krh@6: char *pool; krh@0: krh@78: pool = table->pool->data; krh@78: mask = table->buckets.alloc - 1; krh@6: start = hash_string(key) * sizeof(unsigned long); krh@0: krh@78: for (i = 0; i < table->buckets.alloc; i += sizeof *b) { krh@78: b = table->buckets.data + ((start + i) & mask); krh@6: krh@6: if (*b == 0) krh@0: return 0; krh@0: krh@6: if (strcmp(key, &pool[*b]) == 0) krh@6: return *b; krh@6: } krh@0: krh@0: return 0; krh@0: } krh@0: krh@0: static unsigned long krh@78: add_to_string_pool(struct hashtable *table, const char *key) krh@0: { krh@6: int len; krh@6: char *p; krh@0: krh@0: len = strlen(key) + 1; krh@78: p = array_add(table->pool, len); krh@6: memcpy(p, key, len); krh@0: krh@78: return p - (char *) table->pool->data; krh@0: } krh@0: krh@10: static unsigned long krh@39: add_to_property_pool(struct array *pool, struct array *properties) krh@10: { krh@92: unsigned long *p; krh@92: krh@92: if (properties->size == 0) krh@92: return ~0; krh@92: else if (properties->size == sizeof *p) krh@92: return *(unsigned long *) properties->data | RAZOR_IMMEDIATE; krh@10: krh@39: p = array_add(pool, properties->size); krh@10: memcpy(p, properties->data, properties->size); krh@64: p[properties->size / sizeof *p - 1] |= RAZOR_IMMEDIATE; krh@10: krh@39: return p - (unsigned long *) pool->data; krh@10: } krh@10: krh@0: static void krh@78: do_insert(struct hashtable *table, unsigned long value) krh@0: { krh@6: unsigned int mask, start, i; krh@6: unsigned long *b; krh@0: const char *key; krh@0: krh@78: key = (char *) table->pool->data + value; krh@78: mask = table->buckets.alloc - 1; krh@6: start = hash_string(key) * sizeof(unsigned long); krh@6: krh@78: for (i = 0; i < table->buckets.alloc; i += sizeof *b) { krh@78: b = table->buckets.data + ((start + i) & mask); krh@6: if (*b == 0) { krh@6: *b = value; krh@0: break; krh@0: } krh@6: } krh@0: } krh@0: krh@32: static unsigned long krh@78: hashtable_insert(struct hashtable *table, const char *key) krh@0: { krh@6: unsigned long value, *buckets, *b, *end; krh@6: int alloc; krh@0: krh@78: alloc = table->buckets.alloc; krh@78: array_add(&table->buckets, 4 * sizeof *buckets); krh@78: if (alloc != table->buckets.alloc) { krh@78: end = table->buckets.data + alloc; krh@78: memset(end, 0, table->buckets.alloc - alloc); krh@78: for (b = table->buckets.data; b < end; b++) { krh@6: value = *b; krh@6: if (value != 0) { krh@6: *b = 0; krh@78: do_insert(table, value); krh@6: } krh@0: } krh@0: } krh@0: krh@78: value = add_to_string_pool(table, key); krh@78: do_insert (table, value); krh@0: krh@0: return value; krh@0: } krh@0: krh@78: static void krh@78: hashtable_init(struct hashtable *table, struct array *pool) krh@78: { krh@78: array_init(&table->buckets); krh@78: table->pool = pool; krh@78: } krh@78: krh@78: static void krh@78: hashtable_release(struct hashtable *table) krh@78: { krh@78: array_release(&table->buckets); krh@78: } krh@78: krh@30: static unsigned long krh@79: hashtable_tokenize(struct hashtable *table, const char *string) krh@0: { krh@0: unsigned long token; krh@0: krh@13: if (string == NULL) krh@78: string = ""; krh@13: krh@79: token = hashtable_lookup(table, string); krh@0: if (token != 0) krh@0: return token; krh@0: krh@79: return hashtable_insert(table, string); krh@79: } krh@79: krh@79: static unsigned long krh@79: razor_importer_tokenize(struct razor_importer *importer, const char *string) krh@79: { krh@79: return hashtable_tokenize(&importer->table, string); krh@0: } krh@0: krh@27: void krh@30: razor_importer_begin_package(struct razor_importer *importer, krh@30: const char *name, const char *version) krh@13: { krh@25: struct razor_package *p; krh@13: krh@30: p = array_add(&importer->set->packages, sizeof *p); krh@32: p->name = razor_importer_tokenize(importer, name); krh@32: p->version = razor_importer_tokenize(importer, version); krh@13: krh@30: importer->package = p; krh@66: array_init(&importer->properties); krh@13: } krh@13: krh@13: void krh@30: razor_importer_finish_package(struct razor_importer *importer) krh@13: { krh@25: struct razor_package *p; krh@13: krh@30: p = importer->package; krh@66: p->properties = add_to_property_pool(&importer->set->property_pool, krh@66: &importer->properties); krh@13: krh@66: array_release(&importer->properties); krh@13: } krh@13: krh@66: void krh@30: razor_importer_add_property(struct razor_importer *importer, krh@66: const char *name, const char *version, krh@66: enum razor_property_type type) krh@13: { krh@25: struct razor_property *p; krh@13: unsigned long *r; krh@13: krh@66: p = array_add(&importer->set->properties, sizeof *p); krh@66: p->name = razor_importer_tokenize(importer, name) | (type << 30); krh@32: p->version = razor_importer_tokenize(importer, version); krh@30: p->packages = importer->package - krh@30: (struct razor_package *) importer->set->packages.data; krh@13: krh@66: r = array_add(&importer->properties, sizeof *r); krh@66: *r = p - (struct razor_property *) importer->set->properties.data; krh@30: } krh@30: krh@46: void krh@46: razor_importer_add_file(struct razor_importer *importer, const char *name) krh@46: { krh@52: struct import_entry *e; krh@48: krh@52: e = array_add(&importer->files, sizeof *e); krh@52: krh@52: e->package = importer->package - krh@52: (struct razor_package *) importer->set->packages.data; krh@52: e->name = strdup(name); krh@46: } krh@46: krh@30: struct razor_importer * krh@30: razor_importer_new(void) krh@30: { krh@30: struct razor_importer *importer; krh@30: krh@30: importer = zalloc(sizeof *importer); krh@30: importer->set = razor_set_create(); krh@78: hashtable_init(&importer->table, &importer->set->string_pool); krh@30: krh@30: return importer; krh@9: } krh@9: krh@75: /* Destroy an importer without creating the set. */ krh@75: void krh@75: razor_importer_destroy(struct razor_importer *importer) krh@75: { krh@75: /* FIXME: write this */ krh@75: } krh@75: krh@75: krh@22: typedef int (*compare_with_data_func_t)(const void *p1, krh@22: const void *p, krh@22: void *data); krh@22: krh@25: struct qsort_context { krh@25: size_t size; krh@25: compare_with_data_func_t compare; krh@25: void *data; krh@25: }; krh@25: krh@22: static void krh@22: qsort_swap(void *p1, void *p2, size_t size) krh@22: { krh@22: char buffer[size]; krh@22: krh@22: memcpy(buffer, p1, size); krh@22: memcpy(p1, p2, size); krh@22: memcpy(p2, buffer, size); krh@22: } krh@22: krh@25: static void krh@25: __qsort_with_data(void *base, size_t nelem, unsigned long *map, krh@25: struct qsort_context *ctx) krh@22: { krh@22: void *p, *start, *end, *pivot; krh@25: unsigned long *mp, *mstart, *mend, tmp; krh@22: int left, right, result; krh@25: size_t size = ctx->size; krh@22: krh@22: p = base; krh@22: start = base; krh@22: end = base + nelem * size; krh@25: mp = map; krh@25: mstart = map; krh@25: mend = map + nelem; krh@22: pivot = base + (random() % nelem) * size; krh@25: krh@22: while (p < end) { krh@25: result = ctx->compare(p, pivot, ctx->data); krh@22: if (result < 0) { krh@22: qsort_swap(p, start, size); krh@25: tmp = *mp; krh@25: *mp = *mstart; krh@25: *mstart = tmp; krh@22: if (start == pivot) krh@22: pivot = p; krh@22: start += size; krh@25: mstart++; krh@22: p += size; krh@29: mp++; krh@22: } else if (result == 0) { krh@22: p += size; krh@25: mp++; krh@22: } else { krh@22: end -= size; krh@25: mend--; krh@22: qsort_swap(p, end, size); krh@25: tmp = *mp; krh@29: *mp = *mend; krh@29: *mend = tmp; krh@22: if (end == pivot) krh@22: pivot = p; krh@22: } krh@22: } krh@22: krh@22: left = (start - base) / size; krh@22: right = (base + nelem * size - end) / size; krh@22: if (left > 1) krh@25: __qsort_with_data(base, left, map, ctx); krh@22: if (right > 1) krh@25: __qsort_with_data(end, right, mend, ctx); krh@25: } krh@25: krh@25: unsigned long * krh@25: qsort_with_data(void *base, size_t nelem, size_t size, krh@25: compare_with_data_func_t compare, void *data) krh@25: { krh@25: struct qsort_context ctx; krh@25: unsigned long *map; krh@25: int i; krh@25: krh@75: if (nelem == 0) krh@75: return NULL; krh@75: krh@25: ctx.size = size; krh@25: ctx.compare = compare; krh@25: ctx.data = data; krh@25: krh@25: map = malloc(nelem * sizeof (unsigned long)); krh@25: for (i = 0; i < nelem; i++) krh@25: map[i] = i; krh@25: krh@25: __qsort_with_data(base, nelem, map, &ctx); krh@25: krh@25: return map; krh@22: } krh@9: krh@9: static int krh@35: versioncmp(const char *s1, const char *s2) krh@35: { krh@35: const char *p1, *p2; krh@35: long n1, n2; krh@35: int res; krh@35: krh@35: n1 = strtol(s1, (char **) &p1, 0); krh@35: n2 = strtol(s2, (char **) &p2, 0); krh@35: krh@35: /* Epoch; if one but not the other has an epoch set, default krh@35: * the epoch-less version to 0. */ krh@35: res = (*p1 == ':') - (*p2 == ':'); krh@35: if (res < 0) { krh@35: n1 = 0; krh@35: p1 = s1; krh@35: p2++; krh@35: } else if (res > 0) { krh@35: p1++; krh@35: n2 = 0; krh@35: p2 = s2; krh@35: } krh@35: krh@35: if (n1 != n2) krh@35: return n1 - n2; krh@35: while (*p1 && *p2) { krh@35: if (*p1 != *p2) krh@35: return *p1 - *p2; krh@35: p1++; krh@35: p2++; krh@35: if (isdigit(*p1) && isdigit(*p2)) krh@35: return versioncmp(p1, p2); krh@35: } krh@35: krh@35: return *p1 - *p2; krh@35: } krh@35: krh@35: krh@35: static int krh@22: compare_packages(const void *p1, const void *p2, void *data) krh@9: { krh@25: const struct razor_package *pkg1 = p1, *pkg2 = p2; krh@22: struct razor_set *set = data; krh@22: char *pool = set->string_pool.data; krh@9: krh@23: if (pkg1->name == pkg2->name) krh@35: return versioncmp(&pool[pkg1->version], &pool[pkg2->version]); krh@23: else krh@23: return strcmp(&pool[pkg1->name], &pool[pkg2->name]); krh@9: } krh@9: krh@9: static int krh@22: compare_properties(const void *p1, const void *p2, void *data) krh@9: { krh@25: const struct razor_property *prop1 = p1, *prop2 = p2; krh@22: struct razor_set *set = data; krh@22: char *pool = set->string_pool.data; krh@9: krh@23: if (prop1->name == prop2->name) krh@66: return versioncmp(&pool[prop1->version], krh@66: &pool[prop2->version]); krh@66: else if ((prop1->name & RAZOR_ENTRY_MASK) == (prop2->name & RAZOR_ENTRY_MASK)) krh@66: return (prop1->name >> 30) - (prop2->name >> 30); krh@12: else krh@66: return strcmp(&pool[prop1->name & RAZOR_ENTRY_MASK], krh@66: &pool[prop2->name & RAZOR_ENTRY_MASK]); krh@9: } krh@9: krh@10: static unsigned long * krh@66: uniqueify_properties(struct razor_set *set) krh@9: { krh@25: struct razor_property *rp, *up, *rp_end; krh@18: struct array *pkgs, *p; krh@25: unsigned long *map, *rmap, *r; krh@18: int i, count, unique; krh@9: krh@66: count = set->properties.size / sizeof(struct razor_property); krh@66: map = qsort_with_data(set->properties.data, krh@25: count, krh@25: sizeof(struct razor_property), krh@25: compare_properties, krh@25: set); krh@9: krh@66: rp_end = set->properties.data + set->properties.size; krh@25: rmap = malloc(count * sizeof *map); krh@25: pkgs = zalloc(count * sizeof *pkgs); krh@66: for (rp = set->properties.data, up = rp, i = 0; rp < rp_end; rp++, i++) { krh@25: if (rp->name != up->name || rp->version != up->version) { krh@25: up++; krh@25: up->name = rp->name; krh@25: up->version = rp->version; krh@10: } krh@25: krh@66: unique = up - (struct razor_property *) set->properties.data; krh@25: rmap[map[i]] = unique; krh@25: r = array_add(&pkgs[unique], sizeof *r); krh@25: *r = rp->packages; krh@10: } krh@25: free(map); krh@9: krh@25: up++; krh@66: set->properties.size = (void *) up - set->properties.data; krh@25: rp_end = up; krh@66: for (rp = set->properties.data, p = pkgs; rp < rp_end; rp++, p++) { krh@61: if (p->size / sizeof *r == 1) { krh@61: r = p->data; krh@61: rp->packages = *r | RAZOR_IMMEDIATE; krh@61: } else { krh@61: rp->packages = krh@61: add_to_property_pool(&set->package_pool, p); krh@61: } krh@25: array_release(p); krh@18: } krh@18: krh@18: free(pkgs); krh@18: krh@25: return rmap; krh@10: } krh@10: krh@10: static void krh@39: remap_links(struct array *links, unsigned long *map) krh@10: { krh@39: unsigned long *p, *end; krh@10: krh@39: end = links->data + links->size; krh@39: for (p = links->data; p < end; p++) krh@64: *p = map[*p & RAZOR_ENTRY_MASK] | (*p & ~RAZOR_ENTRY_MASK); krh@9: } krh@9: krh@48: static int krh@48: compare_filenames(const void *p1, const void *p2, void *data) krh@48: { krh@52: const struct import_entry *e1 = p1; krh@52: const struct import_entry *e2 = p2; krh@48: krh@52: return strcmp(e1->name, e2->name); krh@48: } krh@48: krh@48: static void krh@52: count_entries(struct import_directory *d) krh@48: { krh@52: struct import_directory *p, *end; krh@48: krh@48: p = d->files.data; krh@48: end = d->files.data + d->files.size; krh@48: d->count = 0; krh@48: while (p < end) { krh@48: count_entries(p); krh@48: d->count += p->count + 1; krh@48: p++; krh@48: } krh@48: } krh@48: krh@48: static void krh@52: serialize_files(struct razor_set *set, krh@52: struct import_directory *d, struct array *array) krh@48: { krh@52: struct import_directory *p, *end; krh@56: struct razor_entry *e = NULL; krh@60: unsigned long s, *r; krh@48: krh@48: p = d->files.data; krh@48: end = d->files.data + d->files.size; krh@48: s = array->size / sizeof *e + d->files.size / sizeof *p; krh@48: while (p < end) { krh@48: e = array_add(array, sizeof *e); krh@48: e->name = p->name; krh@56: e->start = p->count > 0 ? s : 0; krh@48: s += p->count; krh@60: krh@64: if (p->packages.size == 0) { krh@92: e->packages = ~0; krh@64: } else if (p->packages.size / sizeof *r == 1) { krh@60: r = p->packages.data; krh@60: e->packages = *r | RAZOR_IMMEDIATE; krh@60: } else { krh@60: e->packages = add_to_property_pool(&set->package_pool, krh@60: &p->packages); krh@60: } krh@52: array_release(&p->packages); krh@48: p++; krh@48: } krh@56: if (e != NULL) krh@56: e->name |= RAZOR_ENTRY_LAST; krh@48: krh@48: p = d->files.data; krh@48: end = d->files.data + d->files.size; krh@48: while (p < end) { krh@52: serialize_files(set, p, array); krh@48: p++; krh@48: } krh@48: } krh@48: krh@48: static void krh@61: remap_property_package_links(struct array *properties, unsigned long *rmap) krh@61: { krh@61: struct razor_property *p, *end; krh@61: krh@61: end = properties->data + properties->size; krh@61: for (p = properties->data; p < end; p++) krh@61: if (p->packages & RAZOR_IMMEDIATE) krh@61: p->packages = rmap[p->packages & RAZOR_ENTRY_MASK] | krh@61: RAZOR_IMMEDIATE; krh@61: } krh@61: krh@61: static void krh@48: build_file_tree(struct razor_importer *importer) krh@48: { krh@48: int count, i, length; krh@52: struct import_entry *filenames; krh@52: char *f, *end; krh@52: unsigned long name, *r; krh@48: char dirname[256]; krh@52: struct import_directory *d, root; krh@48: struct razor_entry *e; krh@48: krh@52: count = importer->files.size / sizeof (struct import_entry); krh@48: qsort_with_data(importer->files.data, krh@48: count, krh@52: sizeof (struct import_entry), krh@48: compare_filenames, krh@48: NULL); krh@48: krh@48: root.name = razor_importer_tokenize(importer, ""); krh@48: array_init(&root.files); krh@52: array_init(&root.packages); krh@48: root.last = NULL; krh@48: krh@48: filenames = importer->files.data; krh@48: for (i = 0; i < count; i++) { krh@52: f = filenames[i].name; krh@48: if (*f != '/') krh@48: continue; krh@57: f++; krh@48: krh@48: d = &root; krh@48: while (*f) { krh@57: end = strchr(f, '/'); krh@48: if (end == NULL) krh@48: end = f + strlen(f); krh@57: length = end - f; krh@57: memcpy(dirname, f, length); krh@48: dirname[length] ='\0'; krh@48: name = razor_importer_tokenize(importer, dirname); krh@48: if (d->last == NULL || d->last->name != name) { krh@48: d->last = array_add(&d->files, sizeof *d); krh@48: d->last->name = name; krh@48: d->last->last = NULL; krh@48: array_init(&d->last->files); krh@52: array_init(&d->last->packages); krh@48: } krh@48: d = d->last; krh@57: f = end + 1; krh@57: if (*end == '\0') krh@57: break; krh@48: } krh@48: krh@52: r = array_add(&d->packages, sizeof *r); krh@52: *r = filenames[i].package; krh@52: free(filenames[i].name); krh@48: } krh@48: krh@48: count_entries(&root); krh@56: array_init(&importer->set->files); krh@48: krh@56: e = array_add(&importer->set->files, sizeof *e); krh@56: e->name = root.name | RAZOR_ENTRY_LAST; krh@48: e->start = 1; krh@92: e->packages = ~0; krh@48: krh@56: serialize_files(importer->set, &root, &importer->set->files); krh@48: krh@48: array_release(&importer->files); krh@48: } krh@48: krh@56: static void krh@60: build_package_file_lists(struct razor_set *set, unsigned long *rmap) krh@54: { krh@56: struct razor_package *p, *packages; krh@56: struct array *pkgs; krh@54: struct razor_entry *e, *end; krh@56: unsigned long *r, *q; krh@56: int i, count; krh@54: krh@56: count = set->packages.size / sizeof *p; krh@56: pkgs = zalloc(count * sizeof *pkgs); krh@54: krh@56: end = set->files.data + set->files.size; krh@92: for (e = set->files.data; e < end; e++) { krh@92: if (e->packages == ~0) { krh@92: continue; krh@92: } else if (e->packages & RAZOR_IMMEDIATE) { krh@60: e->packages = rmap[e->packages & RAZOR_ENTRY_MASK] | krh@60: RAZOR_IMMEDIATE; krh@60: r = &e->packages; krh@60: } else { krh@60: r = (unsigned long *) set->package_pool.data + e->packages; krh@60: } krh@60: krh@64: while (1) { krh@60: q = array_add(&pkgs[*r & RAZOR_ENTRY_MASK], sizeof *q); krh@56: *q = e - (struct razor_entry *) set->files.data; krh@60: if (*r++ & RAZOR_IMMEDIATE) krh@60: break; krh@54: } krh@54: } krh@54: krh@56: packages = set->packages.data; krh@56: for (i = 0; i < count; i++) { krh@56: packages[i].files = krh@56: add_to_property_pool(&set->file_pool, &pkgs[i]); krh@56: array_release(&pkgs[i]); krh@48: } krh@56: free(pkgs); krh@52: } krh@52: krh@27: struct razor_set * krh@30: razor_importer_finish(struct razor_importer *importer) krh@9: { krh@30: struct razor_set *set; krh@39: unsigned long *map, *rmap; krh@39: int i, count; krh@18: krh@66: map = uniqueify_properties(importer->set); krh@66: remap_links(&importer->set->property_pool, map); krh@39: free(map); krh@25: krh@30: count = importer->set->packages.size / sizeof(struct razor_package); krh@30: map = qsort_with_data(importer->set->packages.data, krh@25: count, krh@25: sizeof(struct razor_package), krh@25: compare_packages, krh@30: importer->set); krh@39: krh@39: rmap = malloc(count * sizeof *rmap); krh@39: for (i = 0; i < count; i++) krh@39: rmap[map[i]] = i; krh@25: free(map); krh@13: krh@48: build_file_tree(importer); krh@52: remap_links(&importer->set->package_pool, rmap); krh@60: build_package_file_lists(importer->set, rmap); krh@66: remap_property_package_links(&importer->set->properties, rmap); krh@52: free(rmap); krh@48: krh@30: set = importer->set; krh@78: hashtable_release(&importer->table); krh@30: free(importer); krh@30: krh@30: return set; krh@9: } krh@9: krh@92: struct razor_package_iterator { krh@92: struct razor_set *set; krh@92: struct razor_package *package, *end; krh@92: }; krh@92: krh@92: struct razor_package_iterator * krh@92: razor_package_iterator_create(struct razor_set *set) krh@3: { krh@92: struct razor_package_iterator *pi; krh@92: krh@92: pi = zalloc(sizeof *pi); krh@92: pi->set = set; krh@92: pi->package = set->packages.data; krh@92: pi->end = set->packages.data + set->packages.size; krh@92: krh@92: return pi; krh@92: } krh@92: krh@92: int krh@92: razor_package_iterator_next(struct razor_package_iterator *pi, krh@92: struct razor_package **package, krh@92: const char **name, const char **version) krh@92: { krh@6: char *pool; krh@3: krh@92: pool = pi->set->string_pool.data; krh@92: *package = pi->package; krh@92: *name = &pool[pi->package->name]; krh@92: *version = &pool[pi->package->version]; krh@92: krh@92: return pi->package++ < pi->end; krh@92: } krh@92: krh@92: void krh@92: razor_package_iterator_destroy(struct razor_package_iterator *pi) krh@92: { krh@92: free(pi); krh@3: } krh@3: krh@16: struct razor_set *bsearch_set; krh@16: krh@16: static int krh@16: compare_package_name(const void *key, const void *data) krh@16: { krh@16: const struct razor_package *p = data; krh@16: char *pool; krh@16: krh@16: pool = bsearch_set->string_pool.data; krh@16: krh@16: return strcmp(key, &pool[p->name]); krh@16: } krh@16: krh@10: struct razor_package * krh@10: razor_set_get_package(struct razor_set *set, const char *package) krh@10: { krh@16: bsearch_set = set; krh@16: return bsearch(package, set->packages.data, krh@16: set->packages.size / sizeof(struct razor_package), krh@16: sizeof(struct razor_package), compare_package_name); krh@10: } krh@10: krh@18: static int krh@18: compare_property_name(const void *key, const void *data) krh@18: { krh@18: const struct razor_property *p = data; krh@18: char *pool; krh@18: krh@18: pool = bsearch_set->string_pool.data; krh@18: krh@66: return strcmp(key, &pool[p->name & RAZOR_ENTRY_MASK]); krh@18: } krh@18: krh@18: struct razor_property * krh@66: razor_set_get_property(struct razor_set *set, const char *property) krh@18: { krh@18: struct razor_property *p, *start; krh@18: krh@18: bsearch_set = set; krh@66: p = bsearch(property, set->properties.data, krh@66: set->properties.size / sizeof(struct razor_property), krh@18: sizeof(struct razor_property), compare_property_name); krh@18: krh@66: start = set->properties.data; krh@66: while (p > start && krh@66: ((p - 1)->name & RAZOR_ENTRY_MASK) == krh@66: (p->name & RAZOR_ENTRY_MASK)) krh@18: p--; krh@18: krh@18: return p; krh@18: } krh@18: krh@92: struct razor_property_iterator { krh@92: struct razor_set *set; krh@92: struct razor_property *property, *end; krh@92: unsigned long *index; krh@92: int last; krh@92: }; krh@92: krh@92: struct razor_property_iterator * krh@92: razor_property_iterator_create(struct razor_set *set, krh@92: struct razor_package *package) krh@92: { krh@92: struct razor_property_iterator *pi; krh@92: krh@92: pi = zalloc(sizeof *pi); krh@92: pi->set = set; krh@92: pi->property = set->properties.data; krh@92: pi->end = set->properties.data + set->properties.size; krh@92: if (package) { krh@92: pi->index = (unsigned long *) krh@92: set->property_pool.data + package->properties; krh@92: pi->last = 0; krh@92: } else { krh@92: pi->index = NULL; krh@92: pi->last = 0; krh@92: } krh@92: krh@92: return pi; krh@92: } krh@92: krh@92: int krh@92: razor_property_iterator_next(struct razor_property_iterator *pi, krh@92: struct razor_property **property, krh@92: const char **name, const char **version, krh@92: enum razor_property_type *type) krh@92: { krh@92: char *pool; krh@92: int valid; krh@92: struct razor_property *p, *properties; krh@92: krh@92: if (pi->index) { krh@92: properties = pi->set->properties.data; krh@92: p = &properties[*pi->index & RAZOR_ENTRY_MASK]; krh@92: valid = !pi->last; krh@92: pi->last = (*pi->index++ & RAZOR_IMMEDIATE) != 0; krh@92: if (!valid) krh@92: return valid; krh@92: } else { krh@92: p = pi->property++; krh@92: valid = p < pi->end; krh@92: } krh@92: krh@92: pool = pi->set->string_pool.data; krh@92: *property = p; krh@92: *name = &pool[p->name & RAZOR_ENTRY_MASK]; krh@92: *version = &pool[p->version]; krh@92: *type = p->name >> 30; krh@92: krh@92: return valid; krh@92: } krh@92: krh@66: void krh@92: razor_property_iterator_destroy(struct razor_property_iterator *pi) krh@8: { krh@92: free(pi); krh@10: } krh@10: krh@10: void krh@18: razor_set_list_property_packages(struct razor_set *set, krh@20: const char *name, krh@66: const char *version, krh@66: enum razor_property_type type) krh@18: { krh@18: struct razor_property *property, *end; krh@18: struct razor_package *p, *packages; krh@18: unsigned long *r; krh@18: char *pool; krh@18: krh@18: if (name == NULL) krh@18: return; krh@18: krh@66: property = razor_set_get_property(set, name); krh@18: packages = set->packages.data; krh@18: pool = set->string_pool.data; krh@66: end = set->properties.data + set->properties.size; krh@66: while (property < end && krh@66: strcmp(name, &pool[property->name & RAZOR_ENTRY_MASK]) == 0) { krh@66: if (version && krh@66: versioncmp(version, &pool[property->version]) != 0) krh@66: goto next; krh@66: if (type != (property->name >> 30)) krh@20: goto next; krh@61: krh@61: if (property->packages & RAZOR_IMMEDIATE) krh@61: r = &property->packages; krh@61: else krh@61: r = (unsigned long *) krh@61: set->package_pool.data + property->packages; krh@64: while (1) { krh@61: p = &packages[*r & RAZOR_ENTRY_MASK]; krh@66: printf("%s-%s\n", krh@66: &pool[p->name & RAZOR_ENTRY_MASK], krh@66: &pool[p->version]); krh@61: if (*r++ & RAZOR_IMMEDIATE) krh@61: break; krh@18: } krh@20: next: krh@18: property++; krh@18: } krh@18: } krh@18: krh@56: static struct razor_entry * krh@56: find_entry(struct razor_set *set, struct razor_entry *dir, const char *pattern) krh@56: { krh@56: struct razor_entry *e; krh@56: const char *n, *pool = set->string_pool.data; krh@56: int len; krh@56: krh@56: e = (struct razor_entry *) set->files.data + dir->start; krh@56: do { krh@56: n = pool + (e->name & RAZOR_ENTRY_MASK); krh@56: if (strcmp(pattern + 1, n) == 0) krh@56: return e; krh@57: len = strlen(n); krh@56: if (e->start != 0 && strncmp(pattern + 1, n, len) == 0 && krh@56: pattern[len + 1] == '/') { krh@56: return find_entry(set, e, pattern + len + 1); krh@56: } krh@56: } while (((e++)->name & RAZOR_ENTRY_LAST) == 0); krh@56: krh@56: return NULL; krh@56: } krh@56: krh@56: static void krh@56: list_dir(struct razor_set *set, struct razor_entry *dir, krh@56: const char *prefix, const char *pattern) krh@56: { krh@56: struct razor_entry *e; krh@56: const char *n, *pool = set->string_pool.data; krh@56: krh@56: e = (struct razor_entry *) set->files.data + dir->start; krh@56: do { krh@56: n = pool + (e->name & RAZOR_ENTRY_MASK); krh@56: if (pattern && pattern[0] && fnmatch(pattern, n, 0) != 0) krh@56: continue; krh@56: printf("%s/%s%s\n", prefix, n, e->start > 0 ? "/" : ""); krh@56: } while (((e++)->name & RAZOR_ENTRY_LAST) == 0); krh@56: } krh@56: krh@56: void krh@56: razor_set_list_files(struct razor_set *set, const char *pattern) krh@56: { krh@56: struct razor_entry *e; krh@56: char buffer[512], *p, *base; krh@56: krh@56: if (pattern == NULL) krh@56: pattern = "/"; krh@56: krh@56: strcpy(buffer, pattern); krh@56: e = find_entry(set, set->files.data, buffer); krh@56: if (e && e->start > 0) { krh@56: base = NULL; krh@56: } else { krh@56: p = strrchr(buffer, '/'); krh@56: if (p) { krh@56: *p = '\0'; krh@56: base = p + 1; krh@56: } else { krh@56: base = NULL; krh@56: } krh@56: } krh@56: e = find_entry(set, set->files.data, buffer); krh@56: if (e->start != 0) krh@56: list_dir(set, e, buffer, base); krh@56: } krh@56: krh@56: void krh@56: razor_set_list_file_packages(struct razor_set *set, const char *filename) krh@56: { krh@56: struct razor_entry *e; krh@56: struct razor_package *packages, *p; krh@56: const char *pool; krh@56: unsigned long *r; krh@56: krh@56: e = find_entry(set, set->files.data, filename); krh@56: if (e == NULL) krh@56: return; krh@56: krh@60: if (e->packages & RAZOR_IMMEDIATE) krh@60: r = &e->packages; krh@60: else krh@60: r = (unsigned long *) set->package_pool.data + e->packages; krh@60: krh@56: packages = set->packages.data; krh@56: pool = set->string_pool.data; krh@64: while (1) { krh@60: p = &packages[*r & RAZOR_ENTRY_MASK]; krh@59: printf("%s-%s\n", &pool[p->name], &pool[p->version]); krh@60: if (*r++ & RAZOR_IMMEDIATE) krh@60: break; krh@56: } krh@56: } krh@56: krh@56: static unsigned long * krh@56: list_package_files(struct razor_set *set, unsigned long *r, krh@56: struct razor_entry *dir, unsigned long end, krh@56: char *prefix) krh@56: { krh@56: struct razor_entry *e, *f, *entries; krh@83: unsigned long next, file; krh@56: char *pool; krh@56: int len; krh@56: krh@56: entries = (struct razor_entry *) set->files.data; krh@56: pool = set->string_pool.data; krh@56: krh@56: e = entries + dir->start; krh@56: do { krh@83: if (entries + (*r & RAZOR_ENTRY_MASK) == e) { krh@56: printf("%s/%s\n", prefix, krh@56: pool + (e->name & RAZOR_ENTRY_MASK)); krh@83: if (*r & RAZOR_ENTRY_LAST) krh@83: return NULL; krh@56: r++; krh@83: if ((*r & RAZOR_ENTRY_MASK) >= end) krh@83: return r; krh@56: } krh@83: } while (!((e++)->name & RAZOR_ENTRY_LAST)); krh@56: krh@56: e = entries + dir->start; krh@56: do { krh@56: if (e->start == 0) krh@56: continue; krh@56: krh@56: if (e->name & RAZOR_ENTRY_LAST) krh@56: next = end; krh@56: else { krh@56: f = e + 1; krh@56: while (f->start == 0 && !(f->name & RAZOR_ENTRY_LAST)) krh@56: f++; krh@56: if (f->start == 0) krh@56: next = end; krh@56: else krh@56: next = f->start; krh@56: } krh@56: krh@83: file = *r & RAZOR_ENTRY_MASK; krh@83: if (e->start <= file && file < next) { krh@56: len = strlen(prefix); krh@56: prefix[len] = '/'; krh@56: strcpy(prefix + len + 1, krh@56: pool + (e->name & RAZOR_ENTRY_MASK)); krh@56: r = list_package_files(set, r, e, next, prefix); krh@56: prefix[len] = '\0'; krh@56: } krh@83: } while (!((e++)->name & RAZOR_ENTRY_LAST) && r != NULL); krh@56: krh@56: return r; krh@56: } krh@56: krh@56: void krh@56: razor_set_list_package_files(struct razor_set *set, const char *name) krh@56: { krh@56: struct razor_package *package; krh@56: unsigned long *r, end; krh@56: char buffer[512]; krh@56: krh@56: package = razor_set_get_package(set, name); krh@56: krh@56: r = (unsigned long *) set->file_pool.data + package->files; krh@56: end = set->files.size / sizeof (struct razor_entry); krh@56: buffer[0] = '\0'; krh@56: list_package_files(set, r, set->files.data, end, buffer); krh@56: } krh@56: krh@43: static void krh@21: razor_set_validate(struct razor_set *set, struct array *unsatisfied) krh@21: { krh@66: struct razor_property *r, *p, *end; krh@21: unsigned long *u; krh@21: char *pool; krh@21: krh@66: end = set->properties.data + set->properties.size; krh@21: pool = set->string_pool.data; krh@21: krh@66: for (r = set->properties.data, p = r; r < end; r++) { krh@66: if (r->name >> 30 != RAZOR_PROPERTY_REQUIRES) krh@66: continue; krh@66: krh@66: if ((r->name & RAZOR_ENTRY_MASK) != (p->name & RAZOR_ENTRY_MASK)) { krh@66: p = r; krh@66: while (p < end && p->name == r->name) krh@66: p++; krh@66: } krh@37: krh@35: /* If there is more than one version of a provides, krh@35: * seek to the end for the highest version. */ krh@66: /* FIXME: This doesn't work if we have a series of krh@66: * requires a = 1, provides a = 1, requires a = 2, krh@66: * provides a = 2, as the kernel and kernel-devel krh@66: * does.*/ krh@66: while (p + 1 < end && p->name == (p + 1)->name) krh@35: p++; krh@37: krh@37: /* FIXME: We need to track property flags (<, <=, = krh@37: * etc) to properly determine if a requires is krh@37: * satisfied. The current code doesn't track that the krh@37: * requires a = 1 isn't satisfied by a = 2 provides. */ krh@37: krh@66: if (p == end || krh@66: (p->name >> 30) != RAZOR_PROPERTY_PROVIDES || krh@66: (r->name & RAZOR_ENTRY_MASK) != (p->name & RAZOR_ENTRY_MASK) || krh@35: versioncmp(&pool[r->version], &pool[p->version]) > 0) { krh@35: /* FIXME: We ignore file requires for now. */ krh@66: if (pool[r->name & RAZOR_ENTRY_MASK] == '/') krh@35: continue; krh@21: u = array_add(unsatisfied, sizeof *u); krh@66: *u = r - (struct razor_property *) set->properties.data; krh@21: } krh@21: } krh@21: } krh@21: krh@21: void krh@21: razor_set_list_unsatisfied(struct razor_set *set) krh@21: { krh@21: struct array unsatisfied; krh@66: struct razor_property *properties, *r; krh@21: unsigned long *u, *end; krh@21: char *pool; krh@21: krh@21: array_init(&unsatisfied); krh@21: razor_set_validate(set, &unsatisfied); krh@21: krh@21: end = unsatisfied.data + unsatisfied.size; krh@66: properties = set->properties.data; krh@21: pool = set->string_pool.data; krh@21: krh@21: for (u = unsatisfied.data; u < end; u++) { krh@66: r = properties + *u; krh@66: if (pool[r->version] == '\0') krh@66: printf("%ss not satisfied\n", krh@66: &pool[r->name & RAZOR_ENTRY_MASK]); krh@66: else krh@66: printf("%s-%s not satisfied\n", krh@66: &pool[r->name & RAZOR_ENTRY_MASK], krh@66: &pool[r->version]); krh@21: } krh@21: krh@21: array_release(&unsatisfied); krh@21: } krh@21: krh@41: #define UPSTREAM_SOURCE 0x80000000ul krh@41: #define INDEX_MASK 0x00fffffful krh@41: krh@41: struct source { krh@41: struct razor_set *set; krh@66: unsigned long *property_map; krh@41: }; krh@41: krh@79: struct razor_merger { krh@79: struct razor_set *set; krh@79: struct hashtable table; krh@79: struct source source1; krh@79: struct source source2; krh@79: }; krh@79: krh@79: static struct razor_merger * krh@79: razor_merger_create(struct razor_set *set1, struct razor_set *set2) krh@41: { krh@79: struct razor_merger *merger; krh@41: int count; krh@41: size_t size; krh@41: krh@79: merger = zalloc(sizeof *merger); krh@79: merger->set = razor_set_create(); krh@79: hashtable_init(&merger->table, &merger->set->string_pool); krh@41: krh@79: count = set1->properties.size / sizeof (struct razor_property); krh@79: size = count * sizeof merger->source1.property_map[0]; krh@79: merger->source1.property_map = zalloc(size); krh@79: merger->source1.set = set1; krh@79: krh@79: count = set2->properties.size / sizeof (struct razor_property); krh@79: size = count * sizeof merger->source2.property_map[0]; krh@79: merger->source2.property_map = zalloc(size); krh@79: merger->source2.set = set2; krh@79: krh@79: return merger; krh@41: } krh@41: krh@33: static void krh@79: add_package(struct razor_merger *merger, krh@41: struct razor_package *package, struct source *source, krh@41: unsigned long flags) krh@33: { krh@33: char *pool; krh@33: unsigned long *r; krh@41: struct razor_package *p; krh@33: krh@41: pool = source->set->string_pool.data; krh@79: p = array_add(&merger->set->packages, sizeof *p); krh@79: p->name = hashtable_tokenize(&merger->table, &pool[package->name]); krh@41: p->name |= flags; krh@79: p->version = hashtable_tokenize(&merger->table, krh@79: &pool[package->version]); krh@66: p->properties = package->properties; krh@33: krh@66: if (package->properties & RAZOR_IMMEDIATE) krh@66: r = &package->properties; krh@64: else krh@64: r = (unsigned long *) krh@66: source->set->property_pool.data + package->properties; krh@64: while (1) { krh@66: source->property_map[*r & RAZOR_ENTRY_MASK] = 1; krh@64: if (*r++ & RAZOR_IMMEDIATE) krh@64: break; krh@64: } krh@41: } krh@41: krh@41: krh@41: /* Build the new package list sorted by merging the two package lists. krh@79: * Build new string pool as we go. */ krh@41: static void krh@79: merge_packages(struct razor_merger *merger, struct array *packages) krh@41: { krh@41: struct razor_package *upstream_packages, *p, *s, *send; krh@79: struct source *source1, *source2; krh@41: char *spool, *upool; krh@41: unsigned long *u, *uend; krh@41: int cmp; krh@41: krh@79: source1 = &merger->source1; krh@79: source2 = &merger->source2; krh@41: upstream_packages = source2->set->packages.data; krh@41: krh@41: u = packages->data; krh@41: uend = packages->data + packages->size; krh@41: upool = source2->set->string_pool.data; krh@41: krh@41: s = source1->set->packages.data; krh@41: send = source1->set->packages.data + source1->set->packages.size; krh@41: spool = source1->set->string_pool.data; krh@41: krh@41: while (s < send) { krh@41: p = upstream_packages + *u; krh@41: krh@41: if (u < uend) krh@41: cmp = strcmp(&spool[s->name], &upool[p->name]); krh@41: if (u >= uend || cmp < 0) { krh@79: add_package(merger, s, source1, 0); krh@41: s++; krh@41: } else if (cmp == 0) { krh@79: add_package(merger, p, source2, UPSTREAM_SOURCE); krh@41: s++; krh@41: u++; krh@41: } else { krh@79: add_package(merger, p, source2, UPSTREAM_SOURCE); krh@41: u++; krh@41: } krh@41: } krh@41: } krh@41: krh@41: static unsigned long krh@79: add_property(struct razor_merger *merger, krh@69: const char *name, const char *version, int type) krh@41: { krh@41: struct razor_property *p; krh@41: krh@79: p = array_add(&merger->set->properties, sizeof *p); krh@79: p->name = hashtable_tokenize(&merger->table, name) | (type << 30); krh@79: p->version = hashtable_tokenize(&merger->table, version); krh@41: krh@79: return p - (struct razor_property *) merger->set->properties.data; krh@41: } krh@41: krh@41: static void krh@79: merge_properties(struct razor_merger *merger) krh@41: { krh@41: struct razor_property *p1, *p2; krh@79: struct razor_set *set1, *set2; krh@79: unsigned long *map1, *map2; krh@41: int i, j, cmp, count1, count2; krh@41: char *pool1, *pool2; krh@41: krh@79: set1 = merger->source1.set; krh@79: set2 = merger->source2.set; krh@79: map1 = merger->source1.property_map; krh@79: map2 = merger->source2.property_map; krh@79: krh@41: i = 0; krh@41: j = 0; krh@41: pool1 = set1->string_pool.data; krh@41: pool2 = set2->string_pool.data; krh@41: krh@66: count1 = set1->properties.size / sizeof *p1; krh@66: count2 = set2->properties.size / sizeof *p2; krh@41: while (i < count1 || j < count2) { krh@41: if (i < count1 && map1[i] == 0) { krh@41: i++; krh@41: continue; krh@41: } krh@41: if (j < count2 && map2[j] == 0) { krh@41: j++; krh@41: continue; krh@41: } krh@66: p1 = (struct razor_property *) set1->properties.data + i; krh@66: p2 = (struct razor_property *) set2->properties.data + j; krh@41: if (i < count1 && j < count2) krh@66: cmp = strcmp(&pool1[p1->name & RAZOR_ENTRY_MASK], krh@66: &pool2[p2->name & RAZOR_ENTRY_MASK]); krh@41: else if (i < count1) krh@41: cmp = -1; krh@41: else krh@41: cmp = 1; krh@41: if (cmp == 0) krh@41: cmp = versioncmp(&pool1[p1->version], krh@41: &pool2[p2->version]); krh@41: if (cmp < 0) { krh@79: map1[i++] = add_property(merger, krh@66: &pool1[p1->name & RAZOR_ENTRY_MASK], krh@69: &pool1[p1->version], krh@69: (p1->name >> 30)); krh@41: } else if (cmp > 0) { krh@79: map2[j++] = add_property(merger, krh@66: &pool2[p2->name & RAZOR_ENTRY_MASK], krh@69: &pool2[p2->version], krh@69: (p2->name >> 30)); krh@41: } else { krh@79: map1[i++] = map2[j++] = add_property(merger, krh@66: &pool1[p1->name & RAZOR_ENTRY_MASK], krh@69: &pool1[p1->version], krh@69: (p1->name >> 30)); krh@41: } krh@41: } krh@41: } krh@41: krh@41: static unsigned long krh@41: emit_properties(struct array *source_pool, unsigned long index, krh@41: unsigned long *map, struct array *pool) krh@41: { krh@41: unsigned long r, *p, *q; krh@41: krh@41: r = pool->size / sizeof *q; krh@41: p = (unsigned long *) source_pool->data + index; krh@64: while (1) { krh@41: q = array_add(pool, sizeof *q); krh@64: *q = map[*p & RAZOR_ENTRY_MASK] | (*p & ~RAZOR_ENTRY_MASK); krh@64: if (*p++ & RAZOR_ENTRY_LAST) krh@64: break; krh@33: } krh@33: krh@41: return r; krh@41: } krh@41: krh@41: /* Rebuild property->packages maps. We can't just remap these, as a krh@41: * property may have lost or gained a number of packages. Allocate an krh@41: * array per property and loop through the packages and add them to krh@41: * the arrays for their properties. */ krh@41: static void krh@41: rebuild_package_lists(struct razor_set *set) krh@41: { krh@66: struct array *pkgs, *a; krh@41: struct razor_package *pkg, *pkg_end; krh@41: struct razor_property *prop, *prop_end; krh@66: unsigned long *r, *q, *pool; krh@66: int count; krh@41: krh@66: count = set->properties.size / sizeof (struct razor_property); krh@66: pkgs = zalloc(count * sizeof *pkgs); krh@41: pkg_end = set->packages.data + set->packages.size; krh@66: pool = set->property_pool.data; krh@41: krh@41: for (pkg = set->packages.data; pkg < pkg_end; pkg++) { krh@66: for (r = &pool[pkg->properties]; ; r++) { krh@66: q = array_add(&pkgs[*r & RAZOR_ENTRY_MASK], sizeof *q); krh@41: *q = pkg - (struct razor_package *) set->packages.data; krh@64: if (*r & RAZOR_IMMEDIATE) krh@64: break; krh@41: } krh@33: } krh@33: krh@66: prop_end = set->properties.data + set->properties.size; krh@66: a = pkgs; krh@66: for (prop = set->properties.data; prop < prop_end; prop++, a++) { krh@61: if (a->size / sizeof *r == 1) { krh@61: r = a->data; krh@61: prop->packages = *r | RAZOR_IMMEDIATE; krh@61: } else { krh@61: prop->packages = krh@66: add_to_property_pool(&set->property_pool, a); krh@61: } krh@41: array_release(a); krh@41: } krh@66: free(pkgs); krh@33: } krh@33: krh@79: struct razor_set * krh@79: razor_merger_finish(struct razor_merger *merger) krh@79: { krh@79: struct razor_set *result; krh@79: krh@79: result = merger->set; krh@79: hashtable_release(&merger->table); krh@79: free(merger); krh@79: krh@79: return result; krh@79: } krh@79: krh@33: /* Add packages from 'upstream' to 'set'. The packages to add are krh@33: * specified by the 'packages' array, which is a sorted list of krh@33: * package indexes. Returns a newly allocated package set. Does not krh@41: * enforce validity of the resulting package set. krh@41: * krh@41: * This looks more complicated than it is. An easy way to merge two krh@41: * package sets would be to just use a razor_importer, but that krh@41: * requires resorting, and is thus O(n log n). We can do this in a krh@41: * linear sweep, but it gets a little more complicated. krh@41: */ krh@33: struct razor_set * krh@33: razor_set_add(struct razor_set *set, struct razor_set *upstream, krh@33: struct array *packages) krh@33: { krh@79: struct razor_merger *merger; krh@41: struct razor_package *p, *pend; krh@33: krh@79: merger = razor_merger_create(set, upstream); krh@33: krh@79: merge_packages(merger, packages); krh@41: krh@41: /* As we built the package list, we filled out a bitvector of krh@41: * the properties that are referenced by the packages in the krh@41: * new set. Now we do a parallel loop through the properties krh@41: * and emit those marked in the bit vector to the new set. In krh@41: * the process, we update the bit vector to actually map from krh@41: * indices in the old property list to indices in the new krh@41: * property list for both sets. */ krh@41: krh@79: merge_properties(merger); krh@41: krh@41: /* Now we loop through the packages again and emit the krh@41: * property lists, remapped to point to the new properties. */ krh@41: krh@79: pend = merger->set->packages.data + merger->set->packages.size; krh@79: for (p = merger->set->packages.data; p < pend; p++) { krh@41: struct source *src; krh@41: krh@41: if (p->name & UPSTREAM_SOURCE) krh@79: src = &merger->source2; krh@41: else krh@79: src = &merger->source1; krh@41: krh@66: p->properties = emit_properties(&src->set->property_pool, krh@66: p->properties, krh@66: src->property_map, krh@79: &merger->set->property_pool); krh@41: p->name &= INDEX_MASK; krh@33: } krh@33: krh@79: rebuild_package_lists(merger->set); krh@41: krh@79: return razor_merger_finish(merger); krh@33: } krh@33: krh@37: void krh@37: razor_set_satisfy(struct razor_set *set, struct array *unsatisfied, krh@37: struct razor_set *upstream, struct array *list) krh@37: { krh@37: struct razor_property *requires, *r; krh@37: struct razor_property *p, *pend; krh@39: unsigned long *u, *end, *pkg, *package_pool; krh@37: char *pool, *upool; krh@37: krh@37: end = unsatisfied->data + unsatisfied->size; krh@66: requires = set->properties.data; krh@37: pool = set->string_pool.data; krh@37: krh@66: p = upstream->properties.data; krh@66: pend = upstream->properties.data + upstream->properties.size; krh@37: upool = upstream->string_pool.data; krh@39: package_pool = upstream->package_pool.data; krh@37: krh@37: for (u = unsatisfied->data; u < end; u++) { krh@37: r = requires + *u; krh@37: krh@66: while (p < pend && krh@66: strcmp(&pool[r->name & RAZOR_ENTRY_MASK], krh@66: &upool[p->name & RAZOR_ENTRY_MASK]) > 0 && krh@66: (p->name >> 30) != RAZOR_PROPERTY_PROVIDES) krh@37: p++; krh@37: /* If there is more than one version of a provides, krh@37: * seek to the end for the highest version. */ krh@37: while (p + 1 < pend && p->name == (p + 1)->name) krh@37: p++; krh@37: krh@37: if (p == pend || krh@66: strcmp(&pool[r->name & RAZOR_ENTRY_MASK], krh@66: &upool[p->name & RAZOR_ENTRY_MASK]) != 0 || krh@37: versioncmp(&pool[r->version], &upool[p->version]) > 0) { krh@37: /* Do we need to track unsatisfiable requires krh@37: * as we go, or should we just do a krh@37: * razor_set_validate() at the end? */ krh@37: } else { krh@37: pkg = array_add(list, sizeof *pkg); krh@37: /* We just pull in the first package that provides */ krh@61: if (p->packages & RAZOR_IMMEDIATE) krh@61: *pkg = p->packages & RAZOR_ENTRY_MASK; krh@61: else krh@61: *pkg = package_pool[p->packages]; krh@37: } krh@37: } krh@37: } krh@37: krh@38: static void krh@38: find_packages(struct razor_set *set, krh@38: int count, const char **packages, struct array *list) krh@38: { krh@38: struct razor_package *p; krh@38: unsigned long *r; krh@38: int i; krh@38: krh@38: /* FIXME: Sort the packages. */ krh@38: for (i = 0; i < count; i++) { krh@38: p = razor_set_get_package(set, packages[i]); krh@38: r = array_add(list, sizeof *r); krh@38: *r = p - (struct razor_package *) set->packages.data; krh@38: } krh@38: } krh@38: krh@38: static void krh@38: find_all_packages(struct razor_set *set, krh@38: struct razor_set *upstream, struct array *list) krh@38: { krh@38: struct razor_package *p, *u, *pend, *uend; krh@38: unsigned long *r; krh@38: char *pool, *upool; krh@38: krh@38: pend = set->packages.data + set->packages.size; krh@38: pool = set->string_pool.data; krh@38: u = upstream->packages.data; krh@38: uend = upstream->packages.data + upstream->packages.size; krh@38: upool = upstream->string_pool.data; krh@38: krh@38: for (p = set->packages.data; p < pend; p++) { krh@38: while (u < uend && strcmp(&pool[p->name], &upool[u->name]) > 0) krh@38: u++; krh@38: if (strcmp(&pool[p->name], &upool[u->name]) == 0) { krh@38: r = array_add(list, sizeof *r); krh@38: *r = u - (struct razor_package *) upstream->packages.data; krh@38: } krh@38: } krh@38: } krh@38: krh@33: struct razor_set * krh@33: razor_set_update(struct razor_set *set, struct razor_set *upstream, krh@33: int count, const char **packages) krh@33: { krh@37: struct razor_set *new; krh@43: struct razor_package *upackages; krh@37: struct array list, unsatisfied; krh@37: char *pool; krh@38: unsigned long *u, *end; krh@38: int total = 0; krh@33: krh@33: array_init(&list); krh@38: if (count > 0) krh@38: find_packages(upstream, count, packages, &list); krh@38: else krh@38: find_all_packages(set, upstream, &list); krh@33: krh@37: end = list.data + list.size; krh@37: upackages = upstream->packages.data; krh@37: pool = upstream->string_pool.data; krh@38: total += list.size / sizeof *u; krh@37: krh@37: while (list.size > 0) { krh@37: new = razor_set_add(set, upstream, &list); krh@37: array_release(&list); krh@37: razor_set_destroy(set); krh@37: set = new; krh@37: krh@37: array_init(&unsatisfied); krh@37: razor_set_validate(new, &unsatisfied); krh@37: array_init(&list); krh@37: razor_set_satisfy(new, &unsatisfied, upstream, &list); krh@37: array_release(&unsatisfied); krh@37: krh@37: end = list.data + list.size; krh@37: upackages = upstream->packages.data; krh@37: pool = upstream->string_pool.data; krh@38: total += list.size / sizeof *u; krh@37: } krh@37: krh@37: array_release(&list); krh@37: krh@37: return set; krh@33: } krh@33: krh@44: /* The diff order matters. We should sort the packages so that a krh@44: * REMOVE of a package comes before the INSTALL, and so that all krh@44: * requires for a package have been installed before the package. krh@44: **/ krh@44: krh@44: void krh@44: razor_set_diff(struct razor_set *set, struct razor_set *upstream, krh@44: razor_package_callback_t callback, void *data) krh@44: { krh@44: struct razor_package *p, *pend, *u, *uend; krh@44: char *ppool, *upool; krh@44: int res = 0; krh@44: krh@44: p = set->packages.data; krh@44: pend = set->packages.data + set->packages.size; krh@44: ppool = set->string_pool.data; krh@44: krh@44: u = upstream->packages.data; krh@44: uend = upstream->packages.data + upstream->packages.size; krh@44: upool = upstream->string_pool.data; krh@44: krh@44: while (p < pend || u < uend) { krh@44: if (p < pend && u < uend) { krh@44: res = strcmp(&ppool[p->name], &upool[u->name]); krh@44: if (res == 0) krh@44: res = versioncmp(&ppool[p->version], krh@44: &upool[u->version]); krh@44: } krh@44: krh@44: if (u == uend || res < 0) { krh@44: callback(&ppool[p->name], &ppool[p->version], krh@44: NULL, data); krh@44: p++; krh@44: continue; krh@44: } else if (p == pend || res > 0) { krh@44: callback(&upool[u->name], NULL, &upool[u->version], krh@44: data); krh@44: u++; krh@44: continue; krh@44: } else { krh@44: p++; krh@44: u++; krh@44: } krh@44: } krh@44: }