X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Fdata%2Fcase-map.c;h=131e4078796eb8c8c7d4f812499857b03e6ba031;hb=refs%2Fheads%2Fmdd;hp=7a91f497ca9e66f7f5c76fbf9a5d5cd1653b016a;hpb=9f087e7aa4cdff1d5d46d5e188c0017a9d2d0029;p=pspp diff --git a/src/data/case-map.c b/src/data/case-map.c index 7a91f497ca..131e407879 100644 --- a/src/data/case-map.c +++ b/src/data/case-map.c @@ -1,5 +1,5 @@ /* PSPP - a program for statistical analysis. - Copyright (C) 1997-9, 2000, 2006, 2007 Free Software Foundation, Inc. + Copyright (C) 1997-9, 2000, 2006, 2007, 2009, 2011, 2013 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -16,55 +16,60 @@ #include -#include +#include "data/case-map.h" #include #include -#include -#include -#include -#include +#include "data/casereader.h" +#include "data/casewriter.h" +#include "data/dictionary.h" +#include "data/variable.h" +#include "data/case.h" +#include "libpspp/assertion.h" +#include "libpspp/hash-functions.h" +#include "libpspp/hmap.h" -#include "xalloc.h" +#include "gl/xalloc.h" /* A case map. */ struct case_map { - size_t value_cnt; /* Number of values in map. */ - int *map; /* For each destination index, the - corresponding source index. */ + struct caseproto *proto; /* Prototype for output cases. */ + int *map; /* For each destination index, the + corresponding source index. */ }; -/* Creates and returns an empty map. */ +static struct ccase *translate_case (struct ccase *, void *map_); +static bool destroy_case_map (void *map_); + +/* Creates and returns an empty map that outputs cases matching + PROTO. */ static struct case_map * -create_case_map (size_t n) +create_case_map (const struct caseproto *proto) { + size_t n_values = caseproto_get_n_widths (proto); struct case_map *map; size_t i; map = xmalloc (sizeof *map); - map->value_cnt = n; - map->map = xnmalloc (n, sizeof *map->map); - for (i = 0; i < map->value_cnt; i++) + map->proto = caseproto_ref (proto); + map->map = xnmalloc (n_values, sizeof *map->map); + for (i = 0; i < n_values; i++) map->map[i] = -1; return map; } -/* Inserts into MAP a mapping of the CNT values starting at FROM - to the CNT values starting at TO. */ +/* Inserts into MAP a mapping of the value at index FROM in the + source case to the value at index TO in the destination + case. */ static void -insert_mapping (struct case_map *map, size_t from, size_t to, size_t cnt) +insert_mapping (struct case_map *map, size_t from, size_t to) { - size_t i; - - assert (to + cnt <= map->value_cnt); - for (i = 0; i < cnt; i++) - { - assert (map->map[to + i] == -1); - map->map[to + i] = from + i; - } + assert (to < caseproto_get_n_widths (map->proto)); + assert (map->map[to] == -1); + map->map[to] = from; } /* Destroys case map MAP. */ @@ -73,33 +78,101 @@ case_map_destroy (struct case_map *map) { if (map != NULL) { + caseproto_unref (map->proto); free (map->map); free (map); } } -/* Maps from SRC to DST, applying case map MAP. */ -void -case_map_execute (const struct case_map *map, - const struct ccase *src, struct ccase *dst) -{ - size_t dst_idx; +/* If MAP is nonnull, returns a new case that is the result of + applying case map MAP to SRC, and unrefs SRC. - case_create (dst, map->value_cnt); - for (dst_idx = 0; dst_idx < map->value_cnt; dst_idx++) + If MAP is null, returns SRC unchanged. */ +struct ccase * +case_map_execute (const struct case_map *map, struct ccase *src) +{ + if (map != NULL) { - int src_idx = map->map[dst_idx]; - if (src_idx != -1) - *case_data_rw_idx (dst, dst_idx) = *case_data_idx (src, src_idx); + size_t n_values = caseproto_get_n_widths (map->proto); + struct ccase *dst; + size_t dst_idx; + + dst = case_create (map->proto); + for (dst_idx = 0; dst_idx < n_values; dst_idx++) + { + int src_idx = map->map[dst_idx]; + if (src_idx != -1) + value_copy (case_data_rw_idx (dst, dst_idx), case_data_idx (src, src_idx), caseproto_get_width (map->proto, dst_idx)); + } + case_unref (src); + return dst; } + else + return src; +} + +/* Returns the prototype for output cases created by MAP. The + caller must not unref the returned case prototype. */ +const struct caseproto * +case_map_get_proto (const struct case_map *map) +{ + return map->proto; } -/* Returns the number of `union value's in cases created by - MAP. */ -size_t -case_map_get_value_cnt (const struct case_map *map) +/* Creates and returns a new casereader whose cases are produced + by reading from SUBREADER and executing the actions of MAP. + The casereader will have as many `union value's as MAP. When + the new casereader is destroyed, MAP will be destroyed too. + + After this function is called, SUBREADER must not ever again + be referenced directly. It will be destroyed automatically + when the returned casereader is destroyed. */ +struct casereader * +case_map_create_input_translator (struct case_map *map, + struct casereader *subreader) { - return map->value_cnt; + return casereader_create_translator (subreader, + case_map_get_proto (map), + translate_case, + destroy_case_map, + map); +} + +/* Creates and returns a new casewriter. Cases written to the + new casewriter will be passed through MAP and written to + SUBWRITER. The casewriter will have as many `union value's as + MAP. When the new casewriter is destroyed, MAP will be + destroyed too. + + After this function is called, SUBWRITER must not ever again + be referenced directly. It will be destroyed automatically + when the returned casewriter is destroyed. */ +struct casewriter * +case_map_create_output_translator (struct case_map *map, + struct casewriter *subwriter) +{ + return casewriter_create_translator (subwriter, + case_map_get_proto (map), + translate_case, + destroy_case_map, + map); +} + +/* Casereader/casewriter translation callback. */ +static struct ccase * +translate_case (struct ccase *input, void *map_) +{ + struct case_map *map = map_; + return case_map_execute (map, input); +} + +/* Casereader/casewriter destruction callback. */ +static bool +destroy_case_map (void *map_) +{ + struct case_map *map = map_; + case_map_destroy (map); + return true; } /* Creates and returns a case_map that can be used to compact @@ -116,85 +189,137 @@ struct case_map * case_map_to_compact_dict (const struct dictionary *d, unsigned int exclude_classes) { - size_t var_cnt; + size_t n_vars = dict_get_var_cnt (d); + struct caseproto *proto; struct case_map *map; - size_t value_idx; + size_t n_values; size_t i; - assert ((exclude_classes & ~((1u << DC_ORDINARY) - | (1u << DC_SYSTEM) - | (1u << DC_SCRATCH))) == 0); + /* Create the case mapping. */ + proto = dict_get_compacted_proto (d, exclude_classes); + map = create_case_map (proto); + caseproto_unref (proto); - map = create_case_map (dict_count_values (d, exclude_classes)); - var_cnt = dict_get_var_cnt (d); - value_idx = 0; - for (i = 0; i < var_cnt; i++) + /* Add the values to the case mapping. */ + n_values = 0; + for (i = 0; i < n_vars; i++) { struct variable *v = dict_get_var (d, i); - enum dict_class class = dict_class_from_id (var_get_name (v)); - - if (!(exclude_classes & (1u << class))) - { - size_t value_cnt = var_get_value_cnt (v); - insert_mapping (map, var_get_case_index (v), value_idx, value_cnt); - value_idx += value_cnt; - } + if (!(exclude_classes & (1u << var_get_dict_class (v)))) + insert_mapping (map, var_get_case_index (v), n_values++); } - assert (value_idx == map->value_cnt); return map; } + +struct stage_var + { + struct hmap_node hmap_node; /* In struct case_map_stage's 'stage_vars'. */ + const struct variable *var; + int case_index; + }; + +struct case_map_stage + { + const struct dictionary *dict; + struct hmap stage_vars; + }; -/* Prepares dictionary D for producing a case map. Afterward, - the caller may delete, reorder, or rename variables within D - at will before using case_map_from_dict() to produce the case +/* Prepares and returns a "struct case_map_stage" for producing a case map for + DICT. Afterward, the caller may delete, reorder, or rename variables within + DICT at will before using case_map_stage_get_case_map() to produce the case map. - Uses D's aux members, which must otherwise not be in use. */ -void -case_map_prepare_dict (const struct dictionary *d) + The caller must *not* add new variables to DICT. */ +struct case_map_stage * +case_map_stage_create (const struct dictionary *dict) { - size_t var_cnt = dict_get_var_cnt (d); + size_t n_vars = dict_get_var_cnt (dict); + struct case_map_stage *stage; size_t i; - for (i = 0; i < var_cnt; i++) + stage = xmalloc (sizeof *stage); + stage->dict = dict; + hmap_init (&stage->stage_vars); + + for (i = 0; i < n_vars; i++) { - struct variable *v = dict_get_var (d, i); - int *src_fv = xmalloc (sizeof *src_fv); - *src_fv = var_get_case_index (v); - var_attach_aux (v, src_fv, var_dtor_free); + const struct variable *var = dict_get_var (dict, i); + struct stage_var *stage_var; + + stage_var = xmalloc (sizeof *stage_var); + stage_var->var = var; + stage_var->case_index = var_get_case_index (var); + hmap_insert (&stage->stage_vars, &stage_var->hmap_node, + hash_pointer (var, 0)); + } + + return stage; +} + +/* Destroys STAGE, which was created by case_map_stage_create(). */ +void +case_map_stage_destroy (struct case_map_stage *stage) +{ + if (stage != NULL) + { + struct stage_var *stage_var, *next_stage_var; + + HMAP_FOR_EACH_SAFE (stage_var, next_stage_var, + struct stage_var, hmap_node, &stage->stage_vars) + { + hmap_delete (&stage->stage_vars, &stage_var->hmap_node); + free (stage_var); + } + hmap_destroy (&stage->stage_vars); + free (stage); } } -/* Produces a case map from dictionary D, which must have been - previously prepared with case_map_prepare_dict(). +static const struct stage_var * +case_map_stage_find_var (const struct case_map_stage *stage, + const struct variable *var) +{ + const struct stage_var *stage_var; + + HMAP_FOR_EACH_IN_BUCKET (stage_var, struct stage_var, hmap_node, + hash_pointer (var, 0), &stage->stage_vars) + if (stage_var->var == var) + return stage_var; + + /* If the following assertion is reached, it indicates a bug in the + case_map_stage client: the client allowed a new variable to be added to + the dictionary. This is not allowed, because of the risk that the new + varaible might have the same address as an old variable that has been + deleted. */ + NOT_REACHED (); +} - Does not retain any reference to D, and clears the aux members - set up by case_map_prepare_dict(). +/* Produces a case map from STAGE, which must have been previously created with + case_map_stage_create(). The case map maps from the original case index of + the variables in STAGE's dictionary to their current case indexes. - Returns the new case map, or a null pointer if no mapping is - required (that is, no data has changed position). */ + Returns the new case map, or a null pointer if no mapping is required (that + is, no data has changed position). */ struct case_map * -case_map_from_dict (const struct dictionary *d) +case_map_stage_get_case_map (const struct case_map_stage *stage) { struct case_map *map; - size_t var_cnt = dict_get_var_cnt (d); + size_t n_vars = dict_get_var_cnt (stage->dict); + size_t n_values; size_t i; bool identity_map = true; - map = create_case_map (dict_get_next_value_idx (d)); - for (i = 0; i < var_cnt; i++) + map = create_case_map (dict_get_proto (stage->dict)); + for (i = 0; i < n_vars; i++) { - struct variable *v = dict_get_var (d, i); - size_t value_cnt = var_get_value_cnt (v); - int *src_fv = (int *) var_detach_aux (v); + const struct variable *var = dict_get_var (stage->dict, i); + const struct stage_var *stage_var = case_map_stage_find_var (stage, var); - if (var_get_case_index (v) != *src_fv) + if (var_get_case_index (var) != stage_var->case_index) identity_map = false; - insert_mapping (map, *src_fv, var_get_case_index (v), value_cnt); - - free (src_fv); + insert_mapping (map, stage_var->case_index, var_get_case_index (var)); } if (identity_map) @@ -203,8 +328,9 @@ case_map_from_dict (const struct dictionary *d) return NULL; } - while (map->value_cnt > 0 && map->map[map->value_cnt - 1] == -1) - map->value_cnt--; + n_values = caseproto_get_n_widths (map->proto); + while (n_values > 0 && caseproto_get_width (map->proto, n_values - 1) == -1) + map->proto = caseproto_remove_widths (map->proto, --n_values, 1); return map; } @@ -221,14 +347,13 @@ case_map_by_name (const struct dictionary *old, size_t var_cnt = dict_get_var_cnt (new); size_t i; - map = create_case_map (dict_get_next_value_idx (new)); + map = create_case_map (dict_get_proto (new)); for (i = 0; i < var_cnt; i++) { struct variable *nv = dict_get_var (new, i); struct variable *ov = dict_lookup_var_assert (old, var_get_name (nv)); assert (var_get_width (nv) == var_get_width (ov)); - insert_mapping (map, var_get_case_index (ov), var_get_case_index (nv), - var_get_value_cnt (ov)); + insert_mapping (map, var_get_case_index (ov), var_get_case_index (nv)); } return map; } @@ -239,6 +364,6 @@ void case_map_dump (const struct case_map *cm) { int i; - for (i = 0 ; i < cm->value_cnt; ++i ) + for (i = 0 ; i < caseproto_get_n_widths (cm->proto); ++i ) printf ("%d -> %d\n", i, cm->map[i]); }