+ case OVSDB_TYPE_INTEGER:
+ if (atom->integer >= base->u.integer.min
+ && atom->integer <= base->u.integer.max) {
+ return NULL;
+ } else if (base->u.integer.min != INT64_MIN) {
+ if (base->u.integer.max != INT64_MAX) {
+ return ovsdb_error("constraint violation",
+ "%"PRId64" is not in the valid range "
+ "%"PRId64" to %"PRId64" (inclusive)",
+ atom->integer,
+ base->u.integer.min, base->u.integer.max);
+ } else {
+ return ovsdb_error("constraint violation",
+ "%"PRId64" is less than minimum allowed "
+ "value %"PRId64,
+ atom->integer, base->u.integer.min);
+ }
+ } else {
+ return ovsdb_error("constraint violation",
+ "%"PRId64" is greater than maximum allowed "
+ "value %"PRId64,
+ atom->integer, base->u.integer.max);
+ }
+ NOT_REACHED();
+
+ case OVSDB_TYPE_REAL:
+ if (atom->real >= base->u.real.min && atom->real <= base->u.real.max) {
+ return NULL;
+ } else if (base->u.real.min != -DBL_MAX) {
+ if (base->u.real.max != DBL_MAX) {
+ return ovsdb_error("constraint violation",
+ "%.*g is not in the valid range "
+ "%.*g to %.*g (inclusive)",
+ DBL_DIG, atom->real,
+ DBL_DIG, base->u.real.min,
+ DBL_DIG, base->u.real.max);
+ } else {
+ return ovsdb_error("constraint violation",
+ "%.*g is less than minimum allowed "
+ "value %.*g",
+ DBL_DIG, atom->real,
+ DBL_DIG, base->u.real.min);
+ }
+ } else {
+ return ovsdb_error("constraint violation",
+ "%.*g is greater than maximum allowed "
+ "value %.*g",
+ DBL_DIG, atom->real,
+ DBL_DIG, base->u.real.max);
+ }
+ NOT_REACHED();
+
+ case OVSDB_TYPE_BOOLEAN:
+ return NULL;
+
+ case OVSDB_TYPE_STRING:
+ return check_string_constraints(atom->string, &base->u.string);
+
+ case OVSDB_TYPE_UUID:
+ return NULL;
+
+ case OVSDB_N_TYPES:
+ default:
+ NOT_REACHED();
+ }
+}
+\f
+static union ovsdb_atom *
+alloc_default_atoms(enum ovsdb_atomic_type type, size_t n)
+{
+ if (type != OVSDB_TYPE_VOID && n) {
+ union ovsdb_atom *atoms;
+ unsigned int i;
+
+ atoms = xmalloc(n * sizeof *atoms);
+ for (i = 0; i < n; i++) {
+ ovsdb_atom_init_default(&atoms[i], type);
+ }
+ return atoms;
+ } else {
+ /* Avoid wasting memory in the n == 0 case, because xmalloc(0) is
+ * treated as xmalloc(1). */
+ return NULL;
+ }
+}
+
+/* Initializes 'datum' as an empty datum. (An empty datum can be treated as
+ * any type.) */
+void
+ovsdb_datum_init_empty(struct ovsdb_datum *datum)
+{
+ datum->n = 0;
+ datum->keys = NULL;
+ datum->values = NULL;
+}
+
+/* Initializes 'datum' as a datum that has the default value for 'type'.
+ *
+ * The default value for a particular type is as defined in ovsdb/SPECS:
+ *
+ * - If n_min is 0, then the default value is the empty set (or map).
+ *
+ * - If n_min is 1, the default value is a single value or a single
+ * key-value pair, whose key and value are the defaults for their
+ * atomic types. (See ovsdb_atom_init_default() for details.)
+ *
+ * - n_min > 1 is invalid. See ovsdb_type_is_valid().
+ */
+void
+ovsdb_datum_init_default(struct ovsdb_datum *datum,
+ const struct ovsdb_type *type)
+{
+ datum->n = type->n_min;
+ datum->keys = alloc_default_atoms(type->key.type, datum->n);
+ datum->values = alloc_default_atoms(type->value.type, datum->n);
+}
+
+/* Returns a read-only datum of the given 'type' that has the default value for
+ * 'type'. The caller must not modify or free the returned datum.
+ *
+ * See ovsdb_datum_init_default() for an explanation of the default value of a
+ * datum. */
+const struct ovsdb_datum *
+ovsdb_datum_default(const struct ovsdb_type *type)
+{
+ if (type->n_min == 0) {
+ static const struct ovsdb_datum empty;
+ return ∅
+ } else if (type->n_min == 1) {
+ static struct ovsdb_datum default_data[OVSDB_N_TYPES][OVSDB_N_TYPES];
+ struct ovsdb_datum *d;
+ int kt = type->key.type;
+ int vt = type->value.type;
+
+ assert(ovsdb_type_is_valid(type));
+
+ d = &default_data[kt][vt];
+ if (!d->n) {
+ d->n = 1;
+ d->keys = (union ovsdb_atom *) ovsdb_atom_default(kt);
+ if (vt != OVSDB_TYPE_VOID) {
+ d->values = (union ovsdb_atom *) ovsdb_atom_default(vt);
+ }
+ }
+ return d;
+ } else {
+ NOT_REACHED();
+ }
+}
+
+/* Returns true if 'datum', which must have the given 'type', has the default
+ * value for that type.
+ *
+ * See ovsdb_datum_init_default() for an explanation of the default value of a
+ * datum. */
+bool
+ovsdb_datum_is_default(const struct ovsdb_datum *datum,
+ const struct ovsdb_type *type)
+{
+ size_t i;
+
+ if (datum->n != type->n_min) {
+ return false;
+ }
+ for (i = 0; i < datum->n; i++) {
+ if (!ovsdb_atom_is_default(&datum->keys[i], type->key.type)) {
+ return false;
+ }
+ if (type->value.type != OVSDB_TYPE_VOID
+ && !ovsdb_atom_is_default(&datum->values[i], type->value.type)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static union ovsdb_atom *
+clone_atoms(const union ovsdb_atom *old, enum ovsdb_atomic_type type, size_t n)
+{
+ if (type != OVSDB_TYPE_VOID && n) {
+ union ovsdb_atom *new;
+ unsigned int i;
+
+ new = xmalloc(n * sizeof *new);
+ for (i = 0; i < n; i++) {
+ ovsdb_atom_clone(&new[i], &old[i], type);
+ }
+ return new;
+ } else {
+ /* Avoid wasting memory in the n == 0 case, because xmalloc(0) is
+ * treated as xmalloc(1). */
+ return NULL;
+ }
+}
+
+/* Initializes 'new' as a copy of 'old', with the given 'type'.
+ *
+ * The caller must eventually arrange for 'new' to be destroyed (with
+ * ovsdb_datum_destroy()). */
+void
+ovsdb_datum_clone(struct ovsdb_datum *new, const struct ovsdb_datum *old,
+ const struct ovsdb_type *type)
+{
+ unsigned int n = old->n;
+ new->n = n;
+ new->keys = clone_atoms(old->keys, type->key.type, n);
+ new->values = clone_atoms(old->values, type->value.type, n);
+}
+
+static void
+free_data(enum ovsdb_atomic_type type,
+ union ovsdb_atom *atoms, size_t n_atoms)
+{
+ if (ovsdb_atom_needs_destruction(type)) {
+ unsigned int i;
+ for (i = 0; i < n_atoms; i++) {
+ ovsdb_atom_destroy(&atoms[i], type);
+ }
+ }
+ free(atoms);
+}
+
+/* Frees the data owned by 'datum', which must have the given 'type'.
+ *
+ * This does not actually call free(datum). If necessary, the caller must be
+ * responsible for that. */
+void
+ovsdb_datum_destroy(struct ovsdb_datum *datum, const struct ovsdb_type *type)
+{
+ free_data(type->key.type, datum->keys, datum->n);
+ free_data(type->value.type, datum->values, datum->n);
+}
+
+/* Swaps the contents of 'a' and 'b', which need not have the same type. */
+void
+ovsdb_datum_swap(struct ovsdb_datum *a, struct ovsdb_datum *b)
+{
+ struct ovsdb_datum tmp = *a;
+ *a = *b;
+ *b = tmp;
+}
+
+struct ovsdb_datum_sort_cbdata {
+ enum ovsdb_atomic_type key_type;
+ enum ovsdb_atomic_type value_type;
+ struct ovsdb_datum *datum;
+};
+
+static int
+ovsdb_datum_sort_compare_cb(size_t a, size_t b, void *cbdata_)
+{
+ struct ovsdb_datum_sort_cbdata *cbdata = cbdata_;
+ int retval;
+
+ 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
+ovsdb_datum_sort_swap_cb(size_t a, size_t b, void *cbdata_)
+{
+ struct ovsdb_datum_sort_cbdata *cbdata = cbdata_;
+
+ ovsdb_atom_swap(&cbdata->datum->keys[a], &cbdata->datum->keys[b]);
+ if (cbdata->datum->values) {
+ ovsdb_atom_swap(&cbdata->datum->values[a], &cbdata->datum->values[b]);
+ }
+}
+
+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',
+ * whose keys are of type 'key_type'.
+ *
+ * This function returns NULL if successful, otherwise an error message. The
+ * caller must free the returned error when it is no longer needed. On error,
+ * 'datum' is sorted but not unique. */
+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;
+ }
+
+ 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;
+}
+
+/* This function is the same as ovsdb_datum_sort(), except that the caller
+ * knows that 'datum' is unique. The operation therefore "cannot fail", so
+ * this function assert-fails if it actually does. */
+void
+ovsdb_datum_sort_assert(struct ovsdb_datum *datum,
+ enum ovsdb_atomic_type key_type)
+{
+ struct ovsdb_error *error = ovsdb_datum_sort(datum, key_type);
+ if (error) {
+ NOT_REACHED();
+ }
+}
+
+/* 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.
+ *
+ * This function is not commonly useful because the most ordinary way to obtain
+ * a datum is ultimately via ovsdb_atom_from_string() or
+ * ovsdb_atom_from_json(), which check constraints themselves. */
+struct ovsdb_error *
+ovsdb_datum_check_constraints(const struct ovsdb_datum *datum,
+ const struct ovsdb_type *type)
+{
+ struct ovsdb_error *error;
+ unsigned int i;
+
+ for (i = 0; i < datum->n; i++) {
+ error = ovsdb_atom_check_constraints(&datum->keys[i], &type->key);
+ if (error) {
+ return error;
+ }
+ }
+
+ if (type->value.type != OVSDB_TYPE_VOID) {
+ for (i = 0; i < datum->n; i++) {
+ error = ovsdb_atom_check_constraints(&datum->values[i],
+ &type->value);
+ if (error) {
+ return error;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+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;
+
+ if (ovsdb_type_is_map(type)
+ || (json->type == JSON_ARRAY
+ && json->u.array.n > 0
+ && json->u.array.elems[0]->type == JSON_STRING
+ && !strcmp(json->u.array.elems[0]->u.string, "set"))) {
+ bool is_map = ovsdb_type_is_map(type);
+ const char *class = is_map ? "map" : "set";
+ const struct json *inner;
+ unsigned int i;
+ size_t n;
+
+ error = unwrap_json(json, class, JSON_ARRAY, &inner);
+ if (error) {
+ return error;