From 2b66469b96c8a2955d4321fc7caf96be74d33185 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Wed, 16 Jun 2010 13:44:08 -0700 Subject: [PATCH] ovsdb: New functions ovsdb_datum_sort_unique(), ovsdb_datum_from_json_unique(). These new functions are more forgiving than the corresponding functions without "_unique". The goal is to be more tolerant of data provided by IDL clients, which will happen in a followup patch. --- lib/ovsdb-data.c | 171 ++++++++++++++++++++++++++++++++++----------- lib/ovsdb-data.h | 9 +++ tests/test-ovsdb.c | 25 ++++++- 3 files changed, 161 insertions(+), 44 deletions(-) diff --git a/lib/ovsdb-data.c b/lib/ovsdb-data.c index 9691cf432..dba89acb5 100644 --- a/lib/ovsdb-data.c +++ b/lib/ovsdb-data.c @@ -935,6 +935,7 @@ ovsdb_datum_swap(struct ovsdb_datum *a, struct ovsdb_datum *b) struct ovsdb_datum_sort_cbdata { enum ovsdb_atomic_type key_type; + enum ovsdb_atomic_type value_type; struct ovsdb_datum *datum; }; @@ -942,10 +943,18 @@ static int ovsdb_datum_sort_compare_cb(size_t a, size_t b, void *cbdata_) { struct ovsdb_datum_sort_cbdata *cbdata = cbdata_; + int retval; - return ovsdb_atom_compare_3way(&cbdata->datum->keys[a], - &cbdata->datum->keys[b], - cbdata->key_type); + retval = ovsdb_atom_compare_3way(&cbdata->datum->keys[a], + &cbdata->datum->keys[b], + cbdata->key_type); + if (retval || cbdata->value_type == OVSDB_TYPE_VOID) { + return retval; + } + + return ovsdb_atom_compare_3way(&cbdata->datum->values[a], + &cbdata->datum->values[b], + cbdata->value_type); } static void @@ -959,6 +968,19 @@ ovsdb_datum_sort_swap_cb(size_t a, size_t b, void *cbdata_) } } +static void +ovsdb_datum_sort__(struct ovsdb_datum *datum, enum ovsdb_atomic_type key_type, + enum ovsdb_atomic_type value_type) +{ + struct ovsdb_datum_sort_cbdata cbdata; + + cbdata.key_type = key_type; + cbdata.value_type = value_type; + cbdata.datum = datum; + sort(datum->n, ovsdb_datum_sort_compare_cb, ovsdb_datum_sort_swap_cb, + &cbdata); +} + /* The keys in an ovsdb_datum must be unique and in sorted order. Most * functions that modify an ovsdb_datum maintain these invariants. For those * that don't, this function checks and restores these invariants for 'datum', @@ -970,30 +992,25 @@ ovsdb_datum_sort_swap_cb(size_t a, size_t b, void *cbdata_) struct ovsdb_error * ovsdb_datum_sort(struct ovsdb_datum *datum, enum ovsdb_atomic_type key_type) { + size_t i; + if (datum->n < 2) { return NULL; - } else { - struct ovsdb_datum_sort_cbdata cbdata; - size_t i; + } - cbdata.key_type = key_type; - cbdata.datum = datum; - sort(datum->n, ovsdb_datum_sort_compare_cb, ovsdb_datum_sort_swap_cb, - &cbdata); - - for (i = 0; i < datum->n - 1; i++) { - if (ovsdb_atom_equals(&datum->keys[i], &datum->keys[i + 1], - key_type)) { - if (datum->values) { - return ovsdb_error(NULL, "map contains duplicate key"); - } else { - return ovsdb_error(NULL, "set contains duplicate"); - } + ovsdb_datum_sort__(datum, key_type, OVSDB_TYPE_VOID); + + for (i = 0; i < datum->n - 1; i++) { + if (ovsdb_atom_equals(&datum->keys[i], &datum->keys[i + 1], + key_type)) { + if (datum->values) { + return ovsdb_error(NULL, "map contains duplicate key"); + } else { + return ovsdb_error(NULL, "set contains duplicate"); } } - - return NULL; } + return NULL; } /* This function is the same as ovsdb_datum_sort(), except that the caller @@ -1009,6 +1026,46 @@ ovsdb_datum_sort_assert(struct ovsdb_datum *datum, } } +/* This is similar to ovsdb_datum_sort(), except that it drops duplicate keys + * instead of reporting an error. In a map type, the smallest value among a + * group of duplicate pairs is retained and the others are dropped. + * + * Returns the number of keys (or pairs) that were dropped. */ +size_t +ovsdb_datum_sort_unique(struct ovsdb_datum *datum, + enum ovsdb_atomic_type key_type, + enum ovsdb_atomic_type value_type) +{ + size_t src, dst; + + if (datum->n < 2) { + return 0; + } + + ovsdb_datum_sort__(datum, key_type, value_type); + + dst = 1; + for (src = 1; src < datum->n; src++) { + if (ovsdb_atom_equals(&datum->keys[src], &datum->keys[dst - 1], + key_type)) { + ovsdb_atom_destroy(&datum->keys[src], key_type); + if (value_type != OVSDB_TYPE_VOID) { + ovsdb_atom_destroy(&datum->values[src], value_type); + } + } else { + if (src != dst) { + datum->keys[dst] = datum->keys[src]; + if (value_type != OVSDB_TYPE_VOID) { + datum->values[dst] = datum->values[src]; + } + } + dst++; + } + } + datum->n = dst; + return datum->n - src; +} + /* Checks that each of the atoms in 'datum' conforms to the constraints * specified by its 'type'. Returns an error if a constraint is violated, * otherwise a null pointer. @@ -1043,21 +1100,11 @@ ovsdb_datum_check_constraints(const struct ovsdb_datum *datum, return NULL; } -/* Parses 'json' as a datum of the type described by 'type'. If successful, - * returns NULL and initializes 'datum' with the parsed datum. On failure, - * returns an error and the contents of 'datum' are indeterminate. The caller - * is responsible for freeing the error or the datum that is returned. - * - * Violations of constraints expressed by 'type' are treated as errors. - * - * If 'symtab' is nonnull, then named UUIDs in 'symtab' are accepted. Refer to - * ovsdb/SPECS for information about this, and for the syntax that this - * function accepts. */ -struct ovsdb_error * -ovsdb_datum_from_json(struct ovsdb_datum *datum, - const struct ovsdb_type *type, - const struct json *json, - struct ovsdb_symbol_table *symtab) +static struct ovsdb_error * +ovsdb_datum_from_json__(struct ovsdb_datum *datum, + const struct ovsdb_type *type, + const struct json *json, + struct ovsdb_symbol_table *symtab) { struct ovsdb_error *error; @@ -1118,12 +1165,6 @@ ovsdb_datum_from_json(struct ovsdb_datum *datum, datum->n++; } - - error = ovsdb_datum_sort(datum, type->key.type); - if (error) { - goto error; - } - return NULL; error: @@ -1143,6 +1184,52 @@ ovsdb_datum_from_json(struct ovsdb_datum *datum, } } +/* Parses 'json' as a datum of the type described by 'type'. If successful, + * returns NULL and initializes 'datum' with the parsed datum. On failure, + * returns an error and the contents of 'datum' are indeterminate. The caller + * is responsible for freeing the error or the datum that is returned. + * + * Violations of constraints expressed by 'type' are treated as errors. + * + * If 'symtab' is nonnull, then named UUIDs in 'symtab' are accepted. Refer to + * ovsdb/SPECS for information about this, and for the syntax that this + * function accepts. */ +struct ovsdb_error * +ovsdb_datum_from_json(struct ovsdb_datum *datum, + const struct ovsdb_type *type, + const struct json *json, + struct ovsdb_symbol_table *symtab) +{ + struct ovsdb_error *error; + + error = ovsdb_datum_from_json__(datum, type, json, symtab); + if (error) { + return error; + } + + error = ovsdb_datum_sort(datum, type->key.type); + if (error) { + ovsdb_datum_destroy(datum, type); + } + return error; +} + +/* This is the same as ovsdb_datum_from_json(), except that duplicate values + * in a set or map are dropped instead of being treated as an error. */ +struct ovsdb_error * +ovsdb_datum_from_json_unique(struct ovsdb_datum *datum, + const struct ovsdb_type *type, + const struct json *json, + struct ovsdb_symbol_table *symtab) +{ + struct ovsdb_error *error; + + error = ovsdb_datum_from_json__(datum, type, json, symtab); + if (!error) { + ovsdb_datum_sort_unique(datum, type->key.type, type->value.type); + } + return error; +} /* Converts 'datum', of the specified 'type', to JSON format, and returns the * JSON. The caller is responsible for freeing the returned JSON. diff --git a/lib/ovsdb-data.h b/lib/ovsdb-data.h index 4f44fd847..820a4dbb3 100644 --- a/lib/ovsdb-data.h +++ b/lib/ovsdb-data.h @@ -84,6 +84,11 @@ struct ovsdb_error *ovsdb_atom_from_json(union ovsdb_atom *, const struct json *, struct ovsdb_symbol_table *) WARN_UNUSED_RESULT; +struct ovsdb_error *ovsdb_datum_from_json_unique(struct ovsdb_datum *, + const struct ovsdb_type *, + const struct json *, + struct ovsdb_symbol_table *) + WARN_UNUSED_RESULT; struct json *ovsdb_atom_to_json(const union ovsdb_atom *, enum ovsdb_atomic_type); @@ -143,6 +148,10 @@ struct ovsdb_error *ovsdb_datum_sort(struct ovsdb_datum *, void ovsdb_datum_sort_assert(struct ovsdb_datum *, enum ovsdb_atomic_type key_type); +size_t ovsdb_datum_sort_unique(struct ovsdb_datum *, + enum ovsdb_atomic_type key_type, + enum ovsdb_atomic_type value_type); + struct ovsdb_error *ovsdb_datum_check_constraints( const struct ovsdb_datum *, const struct ovsdb_type *) WARN_UNUSED_RESULT; diff --git a/tests/test-ovsdb.c b/tests/test-ovsdb.c index 1c1116e1f..0d7e8c580 100644 --- a/tests/test-ovsdb.c +++ b/tests/test-ovsdb.c @@ -139,6 +139,9 @@ usage(void) " print JSON ATOMs in sorted order\n" " parse-data TYPE DATUM...\n" " parse JSON DATUMs as data of given TYPE, and re-serialize\n" + " parse-data-unique TYPE DATUM...\n" + " parse JSON DATUMs as data of given TYPE, eliminating\n" + " duplicate keys, and re-serialize\n" " parse-data-strings TYPE DATUM...\n" " parse string DATUMs as data of given TYPE, and re-serialize\n" " parse-column NAME OBJECT\n" @@ -478,7 +481,12 @@ do_parse_atom_strings(int argc, char *argv[]) } static void -do_parse_data(int argc, char *argv[]) +do_parse_data__(int argc, char *argv[], + struct ovsdb_error * + (*parse)(struct ovsdb_datum *datum, + const struct ovsdb_type *type, + const struct json *json, + struct ovsdb_symbol_table *symtab)) { struct ovsdb_type type; struct json *json; @@ -492,7 +500,7 @@ do_parse_data(int argc, char *argv[]) struct ovsdb_datum datum; json = unbox_json(parse_json(argv[i])); - check_ovsdb_error(ovsdb_datum_from_json(&datum, &type, json, NULL)); + check_ovsdb_error(parse(&datum, &type, json, NULL)); json_destroy(json); print_and_free_json(ovsdb_datum_to_json(&datum, &type)); @@ -502,6 +510,18 @@ do_parse_data(int argc, char *argv[]) ovsdb_type_destroy(&type); } +static void +do_parse_data(int argc, char *argv[]) +{ + do_parse_data__(argc, argv, ovsdb_datum_from_json); +} + +static void +do_parse_data_unique(int argc, char *argv[]) +{ + do_parse_data__(argc, argv, ovsdb_datum_from_json_unique); +} + static void do_parse_data_strings(int argc, char *argv[]) { @@ -1890,6 +1910,7 @@ static struct command all_commands[] = { { "parse-atoms", 2, INT_MAX, do_parse_atoms }, { "parse-atom-strings", 2, INT_MAX, do_parse_atom_strings }, { "parse-data", 2, INT_MAX, do_parse_data }, + { "parse-data-unique", 2, INT_MAX, do_parse_data_unique }, { "parse-data-strings", 2, INT_MAX, do_parse_data_strings }, { "sort-atoms", 2, 2, do_sort_atoms }, { "parse-column", 2, 2, do_parse_column }, -- 2.43.0