ovsdb: New functions ovsdb_datum_sort_unique(), ovsdb_datum_from_json_unique().
authorBen Pfaff <blp@nicira.com>
Wed, 16 Jun 2010 20:44:08 +0000 (13:44 -0700)
committerBen Pfaff <blp@nicira.com>
Mon, 12 Jul 2010 17:07:36 +0000 (10:07 -0700)
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
lib/ovsdb-data.h
tests/test-ovsdb.c

index 9691cf4320ff057bd30f0637f3543477626cf654..dba89acb588634a33129ed2b2c125fe851c1ecdc 100644 (file)
@@ -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.
index 4f44fd847b7955edee7c4ffb1409e2f437bd1f18..820a4dbb3a9be3a1ee6268c16798ae8a2f604651 100644 (file)
@@ -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;
index 1c1116e1f8cad95ef23b10ca60009b11904171d4..0d7e8c5800a91214ec71d80ab9efe4a71d6b717e 100644 (file)
@@ -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 },