summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorBen Pfaff <blp@nicira.com>2010-02-08 14:09:36 -0800
committerBen Pfaff <blp@nicira.com>2010-02-08 14:16:19 -0800
commitbd76d25d8b3b7d11c5a326e91d784ad2cdeecd45 (patch)
tree688e7370f9794e3c4189c38ff38a59937c9016e1 /lib
parent629cd2f17fedf8d922f61ffd13365d1f4f9b34fe (diff)
downloadopenvswitch-bd76d25d8b3b7d11c5a326e91d784ad2cdeecd45.tar.gz
ovsdb: Add simple constraints.
Diffstat (limited to 'lib')
-rw-r--r--lib/automake.mk1
-rw-r--r--lib/ovsdb-data.c383
-rw-r--r--lib/ovsdb-data.h13
-rw-r--r--lib/ovsdb-idl.c4
-rw-r--r--lib/ovsdb-parser.h3
-rw-r--r--lib/ovsdb-types.c471
-rw-r--r--lib/ovsdb-types.h82
-rw-r--r--lib/pcre.h26
-rw-r--r--lib/unicode.c128
-rw-r--r--lib/unicode.h7
10 files changed, 977 insertions, 141 deletions
diff --git a/lib/automake.mk b/lib/automake.mk
index 51d3c11bf..e5a06487a 100644
--- a/lib/automake.mk
+++ b/lib/automake.mk
@@ -88,6 +88,7 @@ lib_libopenvswitch_a_SOURCES = \
lib/packets.h \
lib/pcap.c \
lib/pcap.h \
+ lib/pcre.h \
lib/poll-loop.c \
lib/poll-loop.h \
lib/port-array.c \
diff --git a/lib/ovsdb-data.c b/lib/ovsdb-data.c
index 8631016ff..e79f84997 100644
--- a/lib/ovsdb-data.c
+++ b/lib/ovsdb-data.c
@@ -29,6 +29,7 @@
#include "json.h"
#include "shash.h"
#include "sort.h"
+#include "unicode.h"
static struct json *
wrap_json(const char *name, struct json *wrapped)
@@ -272,10 +273,10 @@ ovsdb_atom_parse_uuid(struct uuid *uuid, const struct json *json,
return error0;
}
-struct ovsdb_error *
-ovsdb_atom_from_json(union ovsdb_atom *atom, enum ovsdb_atomic_type type,
- const struct json *json,
- const struct ovsdb_symbol_table *symtab)
+static struct ovsdb_error * WARN_UNUSED_RESULT
+ovsdb_atom_from_json__(union ovsdb_atom *atom, enum ovsdb_atomic_type type,
+ const struct json *json,
+ const struct ovsdb_symbol_table *symtab)
{
switch (type) {
case OVSDB_TYPE_VOID:
@@ -327,6 +328,26 @@ ovsdb_atom_from_json(union ovsdb_atom *atom, enum ovsdb_atomic_type type,
ovsdb_atomic_type_to_string(type));
}
+struct ovsdb_error *
+ovsdb_atom_from_json(union ovsdb_atom *atom,
+ const struct ovsdb_base_type *base,
+ const struct json *json,
+ const struct ovsdb_symbol_table *symtab)
+{
+ struct ovsdb_error *error;
+
+ error = ovsdb_atom_from_json__(atom, base->type, json, symtab);
+ if (error) {
+ return error;
+ }
+
+ error = ovsdb_atom_check_constraints(atom, base);
+ if (error) {
+ ovsdb_atom_destroy(atom, base->type);
+ }
+ return error;
+}
+
struct json *
ovsdb_atom_to_json(const union ovsdb_atom *atom, enum ovsdb_atomic_type type)
{
@@ -356,28 +377,9 @@ ovsdb_atom_to_json(const union ovsdb_atom *atom, enum ovsdb_atomic_type type)
}
}
-/* Initializes 'atom' to a value of the given 'type' parsed from 's', which
- * takes one of the following forms:
- *
- * - OVSDB_TYPE_INTEGER: A decimal integer optionally preceded by a sign.
- *
- * - OVSDB_TYPE_REAL: A floating-point number in the format accepted by
- * strtod().
- *
- * - OVSDB_TYPE_BOOLEAN: "true", "yes", "on", "1" for true, or "false",
- * "no", "off", or "0" for false.
- *
- * - OVSDB_TYPE_STRING: A JSON string if it begins with a quote, otherwise
- * an arbitrary string.
- *
- * - OVSDB_TYPE_UUID: A UUID in RFC 4122 format.
- *
- * Returns a null pointer if successful, otherwise an error message describing
- * the problem. The caller is responsible for freeing the error.
- */
-char *
-ovsdb_atom_from_string(union ovsdb_atom *atom, enum ovsdb_atomic_type type,
- const char *s)
+static char *
+ovsdb_atom_from_string__(union ovsdb_atom *atom, enum ovsdb_atomic_type type,
+ const char *s)
{
switch (type) {
case OVSDB_TYPE_VOID:
@@ -451,6 +453,45 @@ ovsdb_atom_from_string(union ovsdb_atom *atom, enum ovsdb_atomic_type type,
return NULL;
}
+/* Initializes 'atom' to a value of type 'base' parsed from 's', which takes
+ * one of the following forms:
+ *
+ * - OVSDB_TYPE_INTEGER: A decimal integer optionally preceded by a sign.
+ *
+ * - OVSDB_TYPE_REAL: A floating-point number in the format accepted by
+ * strtod().
+ *
+ * - OVSDB_TYPE_BOOLEAN: "true", "yes", "on", "1" for true, or "false",
+ * "no", "off", or "0" for false.
+ *
+ * - OVSDB_TYPE_STRING: A JSON string if it begins with a quote, otherwise
+ * an arbitrary string.
+ *
+ * - OVSDB_TYPE_UUID: A UUID in RFC 4122 format.
+ *
+ * Returns a null pointer if successful, otherwise an error message describing
+ * the problem. The caller is responsible for freeing the error.
+ */
+char *
+ovsdb_atom_from_string(union ovsdb_atom *atom,
+ const struct ovsdb_base_type *base, const char *s)
+{
+ struct ovsdb_error *error;
+ char *msg;
+
+ msg = ovsdb_atom_from_string__(atom, base->type, s);
+ if (msg) {
+ return msg;
+ }
+
+ error = ovsdb_atom_check_constraints(atom, base);
+ if (error) {
+ msg = ovsdb_error_to_string(error);
+ ovsdb_error_destroy(error);
+ }
+ return msg;
+}
+
static bool
string_needs_quotes(const char *s)
{
@@ -518,6 +559,141 @@ ovsdb_atom_to_string(const union ovsdb_atom *atom, enum ovsdb_atomic_type type,
NOT_REACHED();
}
}
+
+static struct ovsdb_error *
+check_string_constraints(const char *s,
+ const struct ovsdb_string_constraints *c)
+{
+ size_t n_chars;
+ char *msg;
+
+ msg = utf8_validate(s, &n_chars);
+ if (msg) {
+ struct ovsdb_error *error;
+
+ error = ovsdb_error("constraint violation",
+ "\"%s\" is not a valid UTF-8 string: %s",
+ s, msg);
+ free(msg);
+ return error;
+ }
+
+ if (n_chars < c->minLen) {
+ return ovsdb_error(
+ "constraint violation",
+ "\"%s\" length %zu is less than minimum allowed "
+ "length %u", s, n_chars, c->minLen);
+ } else if (n_chars > c->maxLen) {
+ return ovsdb_error(
+ "constraint violation",
+ "\"%s\" length %zu is greater than maximum allowed "
+ "length %u", s, n_chars, c->maxLen);
+ }
+
+#if HAVE_PCRE
+ if (c->re) {
+ int retval;
+
+ retval = pcre_exec(c->re, NULL, s, strlen(s), 0,
+ PCRE_ANCHORED | PCRE_NO_UTF8_CHECK, NULL, 0);
+ if (retval == PCRE_ERROR_NOMATCH) {
+ if (c->reComment) {
+ return ovsdb_error("constraint violation",
+ "\"%s\" is not a %s", s, c->reComment);
+ } else {
+ return ovsdb_error("constraint violation",
+ "\"%s\" does not match regular expression "
+ "/%s/", s, c->reMatch);
+ }
+ } else if (retval < 0) {
+ /* PCRE doesn't have a function to translate an error code to a
+ * description. Bizarre. See pcreapi(3) for error details. */
+ return ovsdb_error("internal error", "PCRE returned error %d",
+ retval);
+ }
+ }
+#endif /* HAVE_PCRE */
+
+ return NULL;
+}
+
+/* Checks whether 'atom' meets the constraints (if any) defined in 'base'.
+ * (base->type must specify 'atom''s type.) Returns a null pointer if the
+ * constraints are met, otherwise an error that explains the violation. */
+struct ovsdb_error *
+ovsdb_atom_check_constraints(const union ovsdb_atom *atom,
+ const struct ovsdb_base_type *base)
+{
+ switch (base->type) {
+ case OVSDB_TYPE_VOID:
+ NOT_REACHED();
+
+ 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();
+ }
+}
static union ovsdb_atom *
alloc_default_atoms(enum ovsdb_atomic_type type, size_t n)
@@ -551,8 +727,8 @@ 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);
+ datum->keys = alloc_default_atoms(type->key.type, datum->n);
+ datum->values = alloc_default_atoms(type->value.type, datum->n);
}
bool
@@ -565,11 +741,11 @@ ovsdb_datum_is_default(const struct ovsdb_datum *datum,
return false;
}
for (i = 0; i < datum->n; i++) {
- if (!ovsdb_atom_is_default(&datum->keys[i], type->key_type)) {
+ 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)) {
+ if (type->value.type != OVSDB_TYPE_VOID
+ && !ovsdb_atom_is_default(&datum->values[i], type->value.type)) {
return false;
}
}
@@ -602,8 +778,8 @@ ovsdb_datum_clone(struct ovsdb_datum *new, const struct ovsdb_datum *old,
{
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);
+ new->keys = clone_atoms(old->keys, type->key.type, n);
+ new->values = clone_atoms(old->values, type->value.type, n);
}
static void
@@ -622,8 +798,8 @@ free_data(enum ovsdb_atomic_type type,
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);
+ free_data(type->key.type, datum->keys, datum->n);
+ free_data(type->value.type, datum->values, datum->n);
}
void
@@ -646,7 +822,7 @@ ovsdb_datum_sort_compare_cb(size_t a, size_t b, void *cbdata_)
return ovsdb_atom_compare_3way(&cbdata->datum->keys[a],
&cbdata->datum->keys[b],
- cbdata->type->key_type);
+ cbdata->type->key.type);
}
static void
@@ -655,7 +831,7 @@ 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->type->value_type != OVSDB_TYPE_VOID) {
+ if (cbdata->type->value.type != OVSDB_TYPE_VOID) {
ovsdb_atom_swap(&cbdata->datum->values[a], &cbdata->datum->values[b]);
}
}
@@ -676,7 +852,7 @@ ovsdb_datum_sort(struct ovsdb_datum *datum, const struct ovsdb_type *type)
for (i = 0; i < datum->n - 1; i++) {
if (ovsdb_atom_equals(&datum->keys[i], &datum->keys[i + 1],
- type->key_type)) {
+ type->key.type)) {
if (ovsdb_type_is_map(type)) {
return ovsdb_error(NULL, "map contains duplicate key");
} else {
@@ -689,6 +865,40 @@ ovsdb_datum_sort(struct ovsdb_datum *datum, const struct ovsdb_type *type)
}
}
+/* 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;
+}
+
struct ovsdb_error *
ovsdb_datum_from_json(struct ovsdb_datum *datum,
const struct ovsdb_type *type,
@@ -702,7 +912,7 @@ ovsdb_datum_from_json(struct ovsdb_datum *datum,
datum->keys = xmalloc(sizeof *datum->keys);
datum->values = NULL;
- error = ovsdb_atom_from_json(&datum->keys[0], type->key_type,
+ error = ovsdb_atom_from_json(&datum->keys[0], &type->key,
json, symtab);
if (error) {
free(datum->keys);
@@ -746,7 +956,7 @@ ovsdb_datum_from_json(struct ovsdb_datum *datum,
}
}
- error = ovsdb_atom_from_json(&datum->keys[i], type->key_type,
+ error = ovsdb_atom_from_json(&datum->keys[i], &type->key,
key, symtab);
if (error) {
goto error;
@@ -754,9 +964,9 @@ ovsdb_datum_from_json(struct ovsdb_datum *datum,
if (is_map) {
error = ovsdb_atom_from_json(&datum->values[i],
- type->value_type, value, symtab);
+ &type->value, value, symtab);
if (error) {
- ovsdb_atom_destroy(&datum->keys[i], type->key_type);
+ ovsdb_atom_destroy(&datum->keys[i], type->key.type);
goto error;
}
}
@@ -784,14 +994,14 @@ ovsdb_datum_to_json(const struct ovsdb_datum *datum,
/* These tests somewhat tolerate a 'datum' that does not exactly match
* 'type', in particular a datum with 'n' not in the allowed range. */
if (datum->n == 1 && ovsdb_type_is_scalar(type)) {
- return ovsdb_atom_to_json(&datum->keys[0], type->key_type);
- } else if (type->value_type == OVSDB_TYPE_VOID) {
+ return ovsdb_atom_to_json(&datum->keys[0], type->key.type);
+ } else if (type->value.type == OVSDB_TYPE_VOID) {
struct json **elems;
size_t i;
elems = xmalloc(datum->n * sizeof *elems);
for (i = 0; i < datum->n; i++) {
- elems[i] = ovsdb_atom_to_json(&datum->keys[i], type->key_type);
+ elems[i] = ovsdb_atom_to_json(&datum->keys[i], type->key.type);
}
return wrap_json("set", json_array_create(elems, datum->n));
@@ -802,8 +1012,8 @@ ovsdb_datum_to_json(const struct ovsdb_datum *datum,
elems = xmalloc(datum->n * sizeof *elems);
for (i = 0; i < datum->n; i++) {
elems[i] = json_array_create_2(
- ovsdb_atom_to_json(&datum->keys[i], type->key_type),
- ovsdb_atom_to_json(&datum->values[i], type->value_type));
+ ovsdb_atom_to_json(&datum->keys[i], type->key.type),
+ ovsdb_atom_to_json(&datum->values[i], type->value.type));
}
return wrap_json("map", json_array_create(elems, datum->n));
@@ -820,20 +1030,19 @@ skip_spaces(const char *p)
}
static char *
-parse_atom_token(const char **s, enum ovsdb_atomic_type type,
+parse_atom_token(const char **s, const struct ovsdb_base_type *base,
union ovsdb_atom *atom)
{
char *token, *error;
error = ovsdb_token_parse(s, &token);
if (!error) {
- error = ovsdb_atom_from_string(atom, type, token);
+ error = ovsdb_atom_from_string(atom, base, token);
free(token);
}
return error;
}
-
static char *
parse_key_value(const char **s, const struct ovsdb_type *type,
union ovsdb_atom *key, union ovsdb_atom *value)
@@ -841,19 +1050,19 @@ parse_key_value(const char **s, const struct ovsdb_type *type,
const char *start = *s;
char *error;
- error = parse_atom_token(s, type->key_type, key);
- if (!error && type->value_type != OVSDB_TYPE_VOID) {
+ error = parse_atom_token(s, &type->key, key);
+ if (!error && type->value.type != OVSDB_TYPE_VOID) {
*s = skip_spaces(*s);
if (**s == '=') {
(*s)++;
*s = skip_spaces(*s);
- error = parse_atom_token(s, type->value_type, value);
+ error = parse_atom_token(s, &type->value, value);
} else {
error = xasprintf("%s: syntax error at \"%c\" expecting \"=\"",
start, **s);
}
if (error) {
- ovsdb_atom_destroy(key, type->key_type);
+ ovsdb_atom_destroy(key, type->key.type);
}
}
return error;
@@ -863,9 +1072,9 @@ static void
free_key_value(const struct ovsdb_type *type,
union ovsdb_atom *key, union ovsdb_atom *value)
{
- ovsdb_atom_destroy(key, type->key_type);
- if (type->value_type != OVSDB_TYPE_VOID) {
- ovsdb_atom_destroy(value, type->value_type);
+ ovsdb_atom_destroy(key, type->key.type);
+ if (type->value.type != OVSDB_TYPE_VOID) {
+ ovsdb_atom_destroy(value, type->value.type);
}
}
@@ -987,10 +1196,10 @@ ovsdb_datum_to_string(const struct ovsdb_datum *datum,
ds_put_cstr(out, ", ");
}
- ovsdb_atom_to_string(&datum->keys[i], type->key_type, out);
+ ovsdb_atom_to_string(&datum->keys[i], type->key.type, out);
if (is_map) {
ds_put_char(out, '=');
- ovsdb_atom_to_string(&datum->values[i], type->value_type, out);
+ ovsdb_atom_to_string(&datum->values[i], type->value.type, out);
}
}
if (type->n_max > 1 || !datum->n) {
@@ -1016,9 +1225,9 @@ uint32_t
ovsdb_datum_hash(const struct ovsdb_datum *datum,
const struct ovsdb_type *type, uint32_t basis)
{
- basis = hash_atoms(type->key_type, datum->keys, datum->n, basis);
- basis ^= (type->key_type << 24) | (type->value_type << 16) | datum->n;
- basis = hash_atoms(type->value_type, datum->values, datum->n, basis);
+ basis = hash_atoms(type->key.type, datum->keys, datum->n, basis);
+ basis ^= (type->key.type << 24) | (type->value.type << 16) | datum->n;
+ basis = hash_atoms(type->value.type, datum->values, datum->n, basis);
return basis;
}
@@ -1059,18 +1268,18 @@ ovsdb_datum_compare_3way(const struct ovsdb_datum *a,
return a->n < b->n ? -1 : 1;
}
- cmp = atom_arrays_compare_3way(a->keys, b->keys, type->key_type, a->n);
+ cmp = atom_arrays_compare_3way(a->keys, b->keys, type->key.type, a->n);
if (cmp) {
return cmp;
}
- return (type->value_type == OVSDB_TYPE_VOID ? 0
- : atom_arrays_compare_3way(a->values, b->values, type->value_type,
+ return (type->value.type == OVSDB_TYPE_VOID ? 0
+ : atom_arrays_compare_3way(a->values, b->values, type->value.type,
a->n));
}
/* If 'key' is one of the keys in 'datum', returns its index within 'datum',
- * otherwise UINT_MAX. 'key_type' must be the type of the atoms stored in the
+ * otherwise UINT_MAX. 'key.type' must be the type of the atoms stored in the
* 'keys' array in 'datum'.
*/
unsigned int
@@ -1095,7 +1304,7 @@ ovsdb_datum_find_key(const struct ovsdb_datum *datum,
}
/* If 'key' and 'value' is one of the key-value pairs in 'datum', returns its
- * index within 'datum', otherwise UINT_MAX. 'key_type' must be the type of
+ * index within 'datum', otherwise UINT_MAX. 'key.type' must be the type of
* the atoms stored in the 'keys' array in 'datum'. 'value_type' may be the
* type of the 'values' atoms or OVSDB_TYPE_VOID to compare only keys.
*/
@@ -1117,7 +1326,7 @@ ovsdb_datum_find_key_value(const struct ovsdb_datum *datum,
/* If atom 'i' in 'a' is also in 'b', returns its index in 'b', otherwise
* UINT_MAX. 'type' must be the type of 'a' and 'b', except that
- * type->value_type may be set to OVSDB_TYPE_VOID to compare keys but not
+ * type->value.type may be set to OVSDB_TYPE_VOID to compare keys but not
* values. */
static unsigned int
ovsdb_datum_find(const struct ovsdb_datum *a, int i,
@@ -1125,9 +1334,9 @@ ovsdb_datum_find(const struct ovsdb_datum *a, int i,
const struct ovsdb_type *type)
{
return ovsdb_datum_find_key_value(b,
- &a->keys[i], type->key_type,
+ &a->keys[i], type->key.type,
a->values ? &a->values[i] : NULL,
- type->value_type);
+ type->value.type);
}
/* Returns true if every element in 'a' is also in 'b', false otherwise. */
@@ -1167,7 +1376,7 @@ ovsdb_datum_reallocate(struct ovsdb_datum *a, const struct ovsdb_type *type,
unsigned int capacity)
{
a->keys = xrealloc(a->keys, capacity * sizeof *a->keys);
- if (type->value_type != OVSDB_TYPE_VOID) {
+ if (type->value.type != OVSDB_TYPE_VOID) {
a->values = xrealloc(a->values, capacity * sizeof *a->values);
}
}
@@ -1182,10 +1391,10 @@ void
ovsdb_datum_remove_unsafe(struct ovsdb_datum *datum, size_t idx,
const struct ovsdb_type *type)
{
- ovsdb_atom_destroy(&datum->keys[idx], type->key_type);
+ ovsdb_atom_destroy(&datum->keys[idx], type->key.type);
datum->keys[idx] = datum->keys[datum->n - 1];
- if (type->value_type != OVSDB_TYPE_VOID) {
- ovsdb_atom_destroy(&datum->values[idx], type->value_type);
+ if (type->value.type != OVSDB_TYPE_VOID) {
+ ovsdb_atom_destroy(&datum->values[idx], type->value.type);
datum->values[idx] = datum->values[datum->n - 1];
}
datum->n--;
@@ -1208,11 +1417,11 @@ ovsdb_datum_add_unsafe(struct ovsdb_datum *datum,
{
size_t idx = datum->n++;
datum->keys = xrealloc(datum->keys, datum->n * sizeof *datum->keys);
- ovsdb_atom_clone(&datum->keys[idx], key, type->key_type);
- if (type->value_type != OVSDB_TYPE_VOID) {
+ ovsdb_atom_clone(&datum->keys[idx], key, type->key.type);
+ if (type->value.type != OVSDB_TYPE_VOID) {
datum->values = xrealloc(datum->values,
datum->n * sizeof *datum->values);
- ovsdb_atom_clone(&datum->values[idx], value, type->value_type);
+ ovsdb_atom_clone(&datum->values[idx], value, type->value.type);
}
}
@@ -1227,21 +1436,21 @@ ovsdb_datum_union(struct ovsdb_datum *a, const struct ovsdb_datum *b,
for (bi = 0; bi < b->n; bi++) {
unsigned int ai;
- ai = ovsdb_datum_find_key(a, &b->keys[bi], type->key_type);
+ ai = ovsdb_datum_find_key(a, &b->keys[bi], type->key.type);
if (ai == UINT_MAX) {
if (n == a->n) {
ovsdb_datum_reallocate(a, type, a->n + (b->n - bi));
}
- ovsdb_atom_clone(&a->keys[n], &b->keys[bi], type->key_type);
- if (type->value_type != OVSDB_TYPE_VOID) {
+ ovsdb_atom_clone(&a->keys[n], &b->keys[bi], type->key.type);
+ if (type->value.type != OVSDB_TYPE_VOID) {
ovsdb_atom_clone(&a->values[n], &b->values[bi],
- type->value_type);
+ type->value.type);
}
n++;
- } else if (replace && type->value_type != OVSDB_TYPE_VOID) {
- ovsdb_atom_destroy(&a->values[ai], type->value_type);
+ } else if (replace && type->value.type != OVSDB_TYPE_VOID) {
+ ovsdb_atom_destroy(&a->values[ai], type->value.type);
ovsdb_atom_clone(&a->values[ai], &b->values[bi],
- type->value_type);
+ type->value.type);
}
}
if (n != a->n) {
@@ -1260,9 +1469,9 @@ ovsdb_datum_subtract(struct ovsdb_datum *a, const struct ovsdb_type *a_type,
bool changed = false;
size_t i;
- assert(a_type->key_type == b_type->key_type);
- assert(a_type->value_type == b_type->value_type
- || b_type->value_type == OVSDB_TYPE_VOID);
+ assert(a_type->key.type == b_type->key.type);
+ assert(a_type->value.type == b_type->value.type
+ || b_type->value.type == OVSDB_TYPE_VOID);
/* XXX The big-O of this could easily be improved. */
for (i = 0; i < a->n; ) {
diff --git a/lib/ovsdb-data.h b/lib/ovsdb-data.h
index 0638fa1bc..c8e146a73 100644
--- a/lib/ovsdb-data.h
+++ b/lib/ovsdb-data.h
@@ -67,18 +67,22 @@ static inline bool ovsdb_atom_equals(const union ovsdb_atom *a,
}
struct ovsdb_error *ovsdb_atom_from_json(union ovsdb_atom *,
- enum ovsdb_atomic_type,
+ const struct ovsdb_base_type *,
const struct json *,
const struct ovsdb_symbol_table *)
WARN_UNUSED_RESULT;
struct json *ovsdb_atom_to_json(const union ovsdb_atom *,
enum ovsdb_atomic_type);
-char *ovsdb_atom_from_string(union ovsdb_atom *, enum ovsdb_atomic_type,
- const char *)
+char *ovsdb_atom_from_string(union ovsdb_atom *,
+ const struct ovsdb_base_type *, const char *)
WARN_UNUSED_RESULT;
void ovsdb_atom_to_string(const union ovsdb_atom *, enum ovsdb_atomic_type,
struct ds *);
+
+struct ovsdb_error *ovsdb_atom_check_constraints(
+ const union ovsdb_atom *, const struct ovsdb_base_type *)
+ WARN_UNUSED_RESULT;
/* An instance of an OVSDB type (given by struct ovsdb_type).
*
@@ -119,6 +123,9 @@ void ovsdb_datum_swap(struct ovsdb_datum *, struct ovsdb_datum *);
/* Checking and maintaining invariants. */
struct ovsdb_error *ovsdb_datum_sort(struct ovsdb_datum *,
const struct ovsdb_type *);
+struct ovsdb_error *ovsdb_datum_check_constraints(
+ const struct ovsdb_datum *, const struct ovsdb_type *)
+ WARN_UNUSED_RESULT;
/* Type conversion. */
struct ovsdb_error *ovsdb_datum_from_json(struct ovsdb_datum *,
diff --git a/lib/ovsdb-idl.c b/lib/ovsdb-idl.c
index a0b0cc940..53be4ec0e 100644
--- a/lib/ovsdb-idl.c
+++ b/lib/ovsdb-idl.c
@@ -1338,6 +1338,7 @@ ovsdb_idl_txn_write(const struct ovsdb_idl_row *row_,
size_t column_idx = column - class->columns;
assert(row->new != NULL);
+ assert(column_idx < class->n_columns);
if (hmap_node_is_null(&row->txn_node)) {
hmap_insert(&row->table->idl->txn->txn_rows, &row->txn_node,
uuid_hash(&row->uuid));
@@ -1516,6 +1517,7 @@ static bool
ovsdb_idl_txn_process_insert_reply(struct ovsdb_idl_txn_insert *insert,
const struct json_array *results)
{
+ static const struct ovsdb_base_type uuid_type = OVSDB_BASE_UUID_INIT;
struct ovsdb_error *error;
struct json *json_uuid;
union ovsdb_atom uuid;
@@ -1536,7 +1538,7 @@ ovsdb_idl_txn_process_insert_reply(struct ovsdb_idl_txn_insert *insert,
return false;
}
- error = ovsdb_atom_from_json(&uuid, OVSDB_TYPE_UUID, json_uuid, NULL);
+ error = ovsdb_atom_from_json(&uuid, &uuid_type, json_uuid, NULL);
if (error) {
char *s = ovsdb_error_to_string(error);
VLOG_WARN_RL(&syntax_rl, "\"insert\" reply \"uuid\" is not a JSON "
diff --git a/lib/ovsdb-parser.h b/lib/ovsdb-parser.h
index 6efa0a73c..a27563a35 100644
--- a/lib/ovsdb-parser.h
+++ b/lib/ovsdb-parser.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009 Nicira Networks
+/* Copyright (c) 2009, 2010 Nicira Networks
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -70,6 +70,7 @@ bool ovsdb_parser_has_error(const struct ovsdb_parser *);
struct ovsdb_error *ovsdb_parser_get_error(const struct ovsdb_parser *);
struct ovsdb_error *ovsdb_parser_finish(struct ovsdb_parser *)
WARN_UNUSED_RESULT;
+void ovsdb_parser_destroy(struct ovsdb_parser *);
bool ovsdb_parser_is_id(const char *string);
diff --git a/lib/ovsdb-types.c b/lib/ovsdb-types.c
index 659b50db3..ff819609f 100644
--- a/lib/ovsdb-types.c
+++ b/lib/ovsdb-types.c
@@ -17,6 +17,7 @@
#include "ovsdb-types.h"
+#include <float.h>
#include <limits.h>
#include "dynamic-string.h"
@@ -25,16 +26,17 @@
#include "ovsdb-parser.h"
const struct ovsdb_type ovsdb_type_integer =
- OVSDB_TYPE_SCALAR_INITIALIZER(OVSDB_TYPE_INTEGER);
+ OVSDB_TYPE_SCALAR_INITIALIZER(OVSDB_BASE_INTEGER_INIT);
const struct ovsdb_type ovsdb_type_real =
- OVSDB_TYPE_SCALAR_INITIALIZER(OVSDB_TYPE_REAL);
+ OVSDB_TYPE_SCALAR_INITIALIZER(OVSDB_BASE_REAL_INIT);
const struct ovsdb_type ovsdb_type_boolean =
- OVSDB_TYPE_SCALAR_INITIALIZER(OVSDB_TYPE_BOOLEAN);
+ OVSDB_TYPE_SCALAR_INITIALIZER(OVSDB_BASE_BOOLEAN_INIT);
const struct ovsdb_type ovsdb_type_string =
- OVSDB_TYPE_SCALAR_INITIALIZER(OVSDB_TYPE_STRING);
+ OVSDB_TYPE_SCALAR_INITIALIZER(OVSDB_BASE_STRING_INIT);
const struct ovsdb_type ovsdb_type_uuid =
- OVSDB_TYPE_SCALAR_INITIALIZER(OVSDB_TYPE_UUID);
-
+ OVSDB_TYPE_SCALAR_INITIALIZER(OVSDB_BASE_UUID_INIT);
+
+/* ovsdb_atomic_type */
const char *
ovsdb_atomic_type_to_string(enum ovsdb_atomic_type type)
{
@@ -70,18 +72,6 @@ ovsdb_atomic_type_to_json(enum ovsdb_atomic_type type)
}
bool
-ovsdb_type_is_valid(const struct ovsdb_type *type)
-{
- return (type->key_type != OVSDB_TYPE_VOID
- && ovsdb_atomic_type_is_valid(type->key_type)
- && ovsdb_atomic_type_is_valid(type->value_type)
- && type->n_min <= 1
- && type->n_min <= type->n_max
- && (type->value_type == OVSDB_TYPE_VOID
- || ovsdb_atomic_type_is_valid_key(type->key_type)));
-}
-
-bool
ovsdb_atomic_type_from_string(const char *string, enum ovsdb_atomic_type *type)
{
if (!strcmp(string, "integer")) {
@@ -118,6 +108,422 @@ ovsdb_atomic_type_from_json(enum ovsdb_atomic_type *type,
return ovsdb_syntax_error(json, NULL, "atomic-type expected");
}
}
+
+/* ovsdb_base_type */
+
+void
+ovsdb_base_type_init(struct ovsdb_base_type *base, enum ovsdb_atomic_type type)
+{
+ base->type = type;
+
+ switch (base->type) {
+ case OVSDB_TYPE_VOID:
+ break;
+
+ case OVSDB_TYPE_INTEGER:
+ base->u.integer.min = INT64_MIN;
+ base->u.integer.max = INT64_MAX;
+ break;
+
+ case OVSDB_TYPE_REAL:
+ base->u.real.min = -DBL_MAX;
+ base->u.real.max = DBL_MAX;
+ break;
+
+ case OVSDB_TYPE_BOOLEAN:
+ break;
+
+ case OVSDB_TYPE_STRING:
+ base->u.string.re = NULL;
+ base->u.string.reMatch = NULL;
+ base->u.string.reComment = NULL;
+ base->u.string.minLen = 0;
+ base->u.string.maxLen = UINT_MAX;
+ break;
+
+ case OVSDB_TYPE_UUID:
+ break;
+
+ case OVSDB_N_TYPES:
+ NOT_REACHED();
+
+ default:
+ NOT_REACHED();
+ }
+}
+
+void
+ovsdb_base_type_clone(struct ovsdb_base_type *dst,
+ const struct ovsdb_base_type *src)
+{
+ *dst = *src;
+
+ switch (dst->type) {
+ case OVSDB_TYPE_VOID:
+ case OVSDB_TYPE_INTEGER:
+ case OVSDB_TYPE_REAL:
+ case OVSDB_TYPE_BOOLEAN:
+ break;
+
+ case OVSDB_TYPE_STRING:
+ if (dst->u.string.re) {
+ pcre_refcount(dst->u.string.re, 1);
+ }
+ break;
+
+ case OVSDB_TYPE_UUID:
+ break;
+
+ case OVSDB_N_TYPES:
+ default:
+ NOT_REACHED();
+ }
+}
+
+void
+ovsdb_base_type_destroy(struct ovsdb_base_type *base)
+{
+ if (base) {
+ switch (base->type) {
+ case OVSDB_TYPE_VOID:
+ case OVSDB_TYPE_INTEGER:
+ case OVSDB_TYPE_REAL:
+ case OVSDB_TYPE_BOOLEAN:
+ break;
+
+ case OVSDB_TYPE_STRING:
+ if (base->u.string.re && !pcre_refcount(base->u.string.re, -1)) {
+ pcre_free(base->u.string.re);
+ free(base->u.string.reMatch);
+ free(base->u.string.reComment);
+ }
+ break;
+
+ case OVSDB_TYPE_UUID:
+ break;
+
+ case OVSDB_N_TYPES:
+ NOT_REACHED();
+
+ default:
+ NOT_REACHED();
+ }
+ }
+}
+
+bool
+ovsdb_base_type_is_valid(const struct ovsdb_base_type *base)
+{
+ switch (base->type) {
+ case OVSDB_TYPE_VOID:
+ return true;
+
+ case OVSDB_TYPE_INTEGER:
+ return base->u.integer.min <= base->u.integer.max;
+
+ case OVSDB_TYPE_REAL:
+ return base->u.real.min <= base->u.real.max;
+
+ case OVSDB_TYPE_BOOLEAN:
+ return true;
+
+ case OVSDB_TYPE_STRING:
+ return base->u.string.minLen <= base->u.string.maxLen;
+
+ case OVSDB_TYPE_UUID:
+ return true;
+
+ case OVSDB_N_TYPES:
+ default:
+ return false;
+ }
+}
+
+bool
+ovsdb_base_type_has_constraints(const struct ovsdb_base_type *base)
+{
+ switch (base->type) {
+ case OVSDB_TYPE_VOID:
+ NOT_REACHED();
+
+ case OVSDB_TYPE_INTEGER:
+ return (base->u.integer.min != INT64_MIN
+ || base->u.integer.max != INT64_MAX);
+
+ case OVSDB_TYPE_REAL:
+ return (base->u.real.min != -DBL_MAX
+ || base->u.real.max != DBL_MAX);
+
+ case OVSDB_TYPE_BOOLEAN:
+ return false;
+
+ case OVSDB_TYPE_STRING:
+ return (base->u.string.reMatch != NULL
+ || base->u.string.minLen != 0
+ || base->u.string.maxLen != UINT_MAX);
+
+ case OVSDB_TYPE_UUID:
+ return false;
+
+ case OVSDB_N_TYPES:
+ NOT_REACHED();
+
+ default:
+ NOT_REACHED();
+ }
+}
+
+void
+ovsdb_base_type_clear_constraints(struct ovsdb_base_type *base)
+{
+ enum ovsdb_atomic_type type = base->type;
+ ovsdb_base_type_destroy(base);
+ ovsdb_base_type_init(base, type);
+}
+
+struct ovsdb_error *
+ovsdb_base_type_set_regex(struct ovsdb_base_type *base,
+ const char *reMatch, const char *reComment)
+{
+ const char *errorString;
+ const char *pattern;
+ int errorOffset;
+
+ /* Compile pattern, anchoring it at both ends. */
+ pattern = reMatch;
+ if (pattern[0] == '\0' || strchr(pattern, '\0')[-1] != '$') {
+ pattern = xasprintf("%s$", pattern);
+ }
+ base->u.string.re = pcre_compile(pattern, (PCRE_ANCHORED | PCRE_UTF8
+ | PCRE_JAVASCRIPT_COMPAT),
+ &errorString, &errorOffset, NULL);
+ if (pattern != reMatch) {
+ free((char *) pattern);
+ }
+ if (!base->u.string.re) {
+ return ovsdb_syntax_error(NULL, "invalid regular expression",
+ "\"%s\" is not a valid regular "
+ "expression: %s", reMatch, errorString);
+ }
+
+ /* Save regular expression. */
+ pcre_refcount(base->u.string.re, 1);
+ base->u.string.reMatch = xstrdup(reMatch);
+ base->u.string.reComment = reComment ? xstrdup(reComment) : NULL;
+ return NULL;
+}
+
+static struct ovsdb_error *
+parse_optional_uint(struct ovsdb_parser *parser, const char *member,
+ unsigned int *uint)
+{
+ const struct json *json;
+
+ json = ovsdb_parser_member(parser, member, OP_INTEGER | OP_OPTIONAL);
+ if (json) {
+ if (json->u.integer < 0 || json->u.integer > UINT_MAX) {
+ return ovsdb_syntax_error(json, NULL,
+ "%s out of valid range 0 to %u",
+ member, UINT_MAX);
+ }
+ *uint = json->u.integer;
+ }
+ return NULL;
+}
+
+struct ovsdb_error *
+ovsdb_base_type_from_json(struct ovsdb_base_type *base,
+ const struct json *json)
+{
+ struct ovsdb_parser parser;
+ struct ovsdb_error *error;
+ const struct json *type;
+
+ if (json->type == JSON_STRING) {
+ error = ovsdb_atomic_type_from_json(&base->type, json);
+ if (error) {
+ return error;
+ }
+ ovsdb_base_type_init(base, base->type);
+ return NULL;
+ }
+
+ ovsdb_parser_init(&parser, json, "ovsdb type");
+ type = ovsdb_parser_member(&parser, "type", OP_STRING);
+ if (ovsdb_parser_has_error(&parser)) {
+ base->type = OVSDB_TYPE_VOID;
+ return ovsdb_parser_finish(&parser);
+ }
+
+ error = ovsdb_atomic_type_from_json(&base->type, type);
+ if (error) {
+ return error;
+ }
+
+ ovsdb_base_type_init(base, base->type);
+ if (base->type == OVSDB_TYPE_INTEGER) {
+ const struct json *min, *max;
+
+ min = ovsdb_parser_member(&parser, "minInteger",
+ OP_INTEGER | OP_OPTIONAL);
+ max = ovsdb_parser_member(&parser, "maxInteger",
+ OP_INTEGER | OP_OPTIONAL);
+ base->u.integer.min = min ? min->u.integer : INT64_MIN;
+ base->u.integer.max = max ? max->u.integer : INT64_MAX;
+ if (base->u.integer.min > base->u.integer.max) {
+ error = ovsdb_syntax_error(json, NULL,
+ "minInteger exceeds maxInteger");
+ }
+ } else if (base->type == OVSDB_TYPE_REAL) {
+ const struct json *min, *max;
+
+ min = ovsdb_parser_member(&parser, "minReal", OP_NUMBER | OP_OPTIONAL);
+ max = ovsdb_parser_member(&parser, "maxReal", OP_NUMBER | OP_OPTIONAL);
+ base->u.real.min = min ? json_real(min) : -DBL_MAX;
+ base->u.real.max = max ? json_real(max) : DBL_MAX;
+ if (base->u.real.min > base->u.real.max) {
+ error = ovsdb_syntax_error(json, NULL, "minReal exceeds maxReal");
+ }
+ } else if (base->type == OVSDB_TYPE_STRING) {
+ const struct json *reMatch;
+
+ reMatch = ovsdb_parser_member(&parser, "reMatch",
+ OP_STRING | OP_OPTIONAL);
+ if (reMatch) {
+ const struct json *reComment;
+
+ reComment = ovsdb_parser_member(&parser, "reComment",
+ OP_STRING | OP_OPTIONAL);
+ error = ovsdb_base_type_set_regex(
+ base, json_string(reMatch),
+ reComment ? json_string(reComment) : NULL);
+ }
+
+ if (!error) {
+ error = parse_optional_uint(&parser, "minLength",
+ &base->u.string.minLen);
+ }
+ if (!error) {
+ error = parse_optional_uint(&parser, "maxLength",
+ &base->u.string.maxLen);
+ }
+ if (!error && base->u.string.minLen > base->u.string.maxLen) {
+ error = ovsdb_syntax_error(json, NULL,
+ "minLength exceeds maxLength");
+ }
+ }
+
+ if (error) {
+ ovsdb_error_destroy(ovsdb_parser_finish(&parser));
+ } else {
+ error = ovsdb_parser_finish(&parser);
+ }
+ if (error) {
+ ovsdb_base_type_destroy(base);
+ base->type = OVSDB_TYPE_VOID;
+ }
+ return error;
+}
+
+struct json *
+ovsdb_base_type_to_json(const struct ovsdb_base_type *base)
+{
+ struct json *json;
+
+ if (!ovsdb_base_type_has_constraints(base)) {
+ return json_string_create(ovsdb_atomic_type_to_string(base->type));
+ }
+
+ json = json_object_create();
+ json_object_put_string(json, "type",
+ ovsdb_atomic_type_to_string(base->type));
+ switch (base->type) {
+ case OVSDB_TYPE_VOID:
+ NOT_REACHED();
+
+ case OVSDB_TYPE_INTEGER:
+ if (base->u.integer.min != INT64_MIN) {
+ json_object_put(json, "minInteger",
+ json_integer_create(base->u.integer.min));
+ }
+ if (base->u.integer.max != INT64_MAX) {
+ json_object_put(json, "maxInteger",
+ json_integer_create(base->u.integer.max));
+ }
+ break;
+
+ case OVSDB_TYPE_REAL:
+ if (base->u.real.min != -DBL_MAX) {
+ json_object_put(json, "minReal",
+ json_real_create(base->u.real.min));
+ }
+ if (base->u.real.max != DBL_MAX) {
+ json_object_put(json, "maxReal",
+ json_real_create(base->u.real.max));
+ }
+ break;
+
+ case OVSDB_TYPE_BOOLEAN:
+ break;
+
+ case OVSDB_TYPE_STRING:
+ if (base->u.string.reMatch) {
+ json_object_put_string(json, "reMatch", base->u.string.reMatch);
+ if (base->u.string.reComment) {
+ json_object_put_string(json, "reComment",
+ base->u.string.reComment);
+ }
+ }
+ if (base->u.string.minLen != 0) {
+ json_object_put(json, "minLength",
+ json_integer_create(base->u.string.minLen));
+ }
+ if (base->u.string.maxLen != UINT_MAX) {
+ json_object_put(json, "maxLength",
+ json_integer_create(base->u.string.maxLen));
+ }
+ break;
+
+ case OVSDB_TYPE_UUID:
+ break;
+
+ case OVSDB_N_TYPES:
+ NOT_REACHED();
+
+ default:
+ NOT_REACHED();
+ }
+
+ return json;
+}
+
+/* ovsdb_type */
+
+void
+ovsdb_type_clone(struct ovsdb_type *dst, const struct ovsdb_type *src)
+{
+ ovsdb_base_type_clone(&dst->key, &src->key);
+ ovsdb_base_type_clone(&dst->value, &src->value);
+ dst->n_min = src->n_min;
+ dst->n_max = src->n_max;
+}
+
+void
+ovsdb_type_destroy(struct ovsdb_type *type)
+{
+ ovsdb_base_type_destroy(&type->key);
+ ovsdb_base_type_destroy(&type->value);
+}
+
+bool
+ovsdb_type_is_valid(const struct ovsdb_type *type)
+{
+ return (type->key.type != OVSDB_TYPE_VOID
+ && ovsdb_base_type_is_valid(&type->key)
+ && ovsdb_base_type_is_valid(&type->value)
+ && type->n_min <= 1
+ && type->n_min <= type->n_max);
+}
static struct ovsdb_error *
n_from_json(const struct json *json, unsigned int *n)
@@ -136,8 +542,8 @@ n_from_json(const struct json *json, unsigned int *n)
char *
ovsdb_type_to_english(const struct ovsdb_type *type)
{
- const char *key = ovsdb_atomic_type_to_string(type->key_type);
- const char *value = ovsdb_atomic_type_to_string(type->value_type);
+ const char *key = ovsdb_atomic_type_to_string(type->key.type);
+ const char *value = ovsdb_atomic_type_to_string(type->value.type);
if (ovsdb_type_is_scalar(type)) {
return xstrdup(key);
} else {
@@ -166,20 +572,21 @@ ovsdb_type_to_english(const struct ovsdb_type *type)
struct ovsdb_error *
ovsdb_type_from_json(struct ovsdb_type *type, const struct json *json)
{
- type->value_type = OVSDB_TYPE_VOID;
+ type->value.type = OVSDB_TYPE_VOID;
type->n_min = 1;
type->n_max = 1;
if (json->type == JSON_STRING) {
- return ovsdb_atomic_type_from_json(&type->key_type, json);
+ return ovsdb_base_type_from_json(&type->key, json);
} else if (json->type == JSON_OBJECT) {
const struct json *key, *value, *min, *max;
struct ovsdb_error *error;
struct ovsdb_parser parser;
ovsdb_parser_init(&parser, json, "ovsdb type");
- key = ovsdb_parser_member(&parser, "key", OP_STRING);
- value = ovsdb_parser_member(&parser, "value", OP_STRING | OP_OPTIONAL);
+ key = ovsdb_parser_member(&parser, "key", OP_STRING | OP_OBJECT);
+ value = ovsdb_parser_member(&parser, "value",
+ OP_STRING | OP_OBJECT | OP_OPTIONAL);
min = ovsdb_parser_member(&parser, "min", OP_INTEGER | OP_OPTIONAL);
max = ovsdb_parser_member(&parser, "max",
OP_INTEGER | OP_STRING | OP_OPTIONAL);
@@ -188,13 +595,13 @@ ovsdb_type_from_json(struct ovsdb_type *type, const struct json *json)
return error;
}
- error = ovsdb_atomic_type_from_json(&type->key_type, key);
+ error = ovsdb_base_type_from_json(&type->key, key);
if (error) {
return error;
}
if (value) {
- error = ovsdb_atomic_type_from_json(&type->value_type, value);
+ error = ovsdb_base_type_from_json(&type->value, value);
if (error) {
return error;
}
@@ -229,15 +636,15 @@ ovsdb_type_from_json(struct ovsdb_type *type, const struct json *json)
struct json *
ovsdb_type_to_json(const struct ovsdb_type *type)
{
- if (ovsdb_type_is_scalar(type)) {
- return ovsdb_atomic_type_to_json(type->key_type);
+ if (ovsdb_type_is_scalar(type)
+ && !ovsdb_base_type_has_constraints(&type->key)) {
+ return ovsdb_base_type_to_json(&type->key);
} else {
struct json *json = json_object_create();
- json_object_put(json, "key",
- ovsdb_atomic_type_to_json(type->key_type));
- if (type->value_type != OVSDB_TYPE_VOID) {
+ json_object_put(json, "key", ovsdb_base_type_to_json(&type->key));
+ if (type->value.type != OVSDB_TYPE_VOID) {
json_object_put(json, "value",
- ovsdb_atomic_type_to_json(type->value_type));
+ ovsdb_base_type_to_json(&type->value));
}
if (type->n_min != 1) {
json_object_put(json, "min", json_integer_create(type->n_min));
diff --git a/lib/ovsdb-types.h b/lib/ovsdb-types.h
index b9b3d5a02..633b50f66 100644
--- a/lib/ovsdb-types.h
+++ b/lib/ovsdb-types.h
@@ -16,6 +16,8 @@
#ifndef OVSDB_TYPES_H
#define OVSDB_TYPES_H 1
+#include <float.h>
+#include <pcre.h>
#include <stdbool.h>
#include <stdint.h>
#include "compiler.h"
@@ -35,13 +37,68 @@ enum ovsdb_atomic_type {
};
static inline bool ovsdb_atomic_type_is_valid(enum ovsdb_atomic_type);
-static inline bool ovsdb_atomic_type_is_valid_key(enum ovsdb_atomic_type);
bool ovsdb_atomic_type_from_string(const char *, enum ovsdb_atomic_type *);
struct ovsdb_error *ovsdb_atomic_type_from_json(enum ovsdb_atomic_type *,
const struct json *);
const char *ovsdb_atomic_type_to_string(enum ovsdb_atomic_type);
struct json *ovsdb_atomic_type_to_json(enum ovsdb_atomic_type);
+/* An atomic type plus optional constraints. */
+
+struct ovsdb_base_type {
+ enum ovsdb_atomic_type type;
+ union {
+ struct ovsdb_integer_constraints {
+ int64_t min; /* minInteger or INT64_MIN. */
+ int64_t max; /* maxInteger or INT64_MAX. */
+ } integer;
+
+ struct ovsdb_real_constraints {
+ double min; /* minReal or -DBL_MAX. */
+ double max; /* minReal or DBL_MAX. */
+ } real;
+
+ /* No constraints for Boolean types. */
+
+ struct ovsdb_string_constraints {
+ pcre *re; /* Compiled regular expression. */
+ char *reMatch; /* reMatch or NULL. */
+ char *reComment; /* reComment or NULL. */
+ unsigned int minLen; /* minLength or 0. */
+ unsigned int maxLen; /* maxLength or UINT_MAX. */
+ } string;
+ } u;
+};
+
+#define OVSDB_BASE_VOID_INIT { .type = OVSDB_TYPE_VOID }
+#define OVSDB_BASE_INTEGER_INIT { .type = OVSDB_TYPE_INTEGER, \
+ .u.integer = { INT64_MIN, INT64_MAX } }
+#define OVSDB_BASE_REAL_INIT { .type = OVSDB_TYPE_REAL, \
+ .u.real = { -DBL_MAX, DBL_MAX } }
+#define OVSDB_BASE_BOOLEAN_INIT { .type = OVSDB_TYPE_BOOLEAN }
+#define OVSDB_BASE_STRING_INIT { .type = OVSDB_TYPE_STRING, \
+ .u.string = { NULL, NULL, NULL, \
+ 0, UINT_MAX } }
+#define OVSDB_BASE_UUID_INIT { .type = OVSDB_TYPE_UUID }
+
+void ovsdb_base_type_init(struct ovsdb_base_type *, enum ovsdb_atomic_type);
+void ovsdb_base_type_clone(struct ovsdb_base_type *,
+ const struct ovsdb_base_type *);
+void ovsdb_base_type_destroy(struct ovsdb_base_type *);
+
+bool ovsdb_base_type_is_valid(const struct ovsdb_base_type *);
+bool ovsdb_base_type_has_constraints(const struct ovsdb_base_type *);
+void ovsdb_base_type_clear_constraints(struct ovsdb_base_type *);
+struct ovsdb_error *ovsdb_base_type_set_regex(struct ovsdb_base_type *,
+ const char *reMatch,
+ const char *reComment)
+ WARN_UNUSED_RESULT;
+
+struct ovsdb_error *ovsdb_base_type_from_json(struct ovsdb_base_type *,
+ const struct json *)
+ WARN_UNUSED_RESULT;
+struct json *ovsdb_base_type_to_json(const struct ovsdb_base_type *);
+
/* An OVSDB type.
*
* Several rules constrain the valid types. See ovsdb_type_is_valid() (in
@@ -60,14 +117,13 @@ struct json *ovsdb_atomic_type_to_json(enum ovsdb_atomic_type);
* can also be considered an optional pair of 'key_type' and 'value_type'.
*/
struct ovsdb_type {
- enum ovsdb_atomic_type key_type;
- enum ovsdb_atomic_type value_type;
+ struct ovsdb_base_type key;
+ struct ovsdb_base_type value;
unsigned int n_min;
unsigned int n_max; /* UINT_MAX stands in for "unlimited". */
};
-#define OVSDB_TYPE_SCALAR_INITIALIZER(KEY_TYPE) \
- { KEY_TYPE, OVSDB_TYPE_VOID, 1, 1 }
+#define OVSDB_TYPE_SCALAR_INITIALIZER(KEY) { KEY, OVSDB_BASE_VOID_INIT, 1, 1 }
extern const struct ovsdb_type ovsdb_type_integer;
extern const struct ovsdb_type ovsdb_type_real;
@@ -75,6 +131,9 @@ extern const struct ovsdb_type ovsdb_type_boolean;
extern const struct ovsdb_type ovsdb_type_string;
extern const struct ovsdb_type ovsdb_type_uuid;
+void ovsdb_type_clone(struct ovsdb_type *, const struct ovsdb_type *);
+void ovsdb_type_destroy(struct ovsdb_type *);
+
bool ovsdb_type_is_valid(const struct ovsdb_type *);
static inline bool ovsdb_type_is_scalar(const struct ovsdb_type *);
@@ -98,16 +157,9 @@ ovsdb_atomic_type_is_valid(enum ovsdb_atomic_type atomic_type)
return atomic_type >= 0 && atomic_type < OVSDB_N_TYPES;
}
-static inline bool
-ovsdb_atomic_type_is_valid_key(enum ovsdb_atomic_type atomic_type)
-{
- /* XXX should we disallow reals or booleans as keys? */
- return ovsdb_atomic_type_is_valid(atomic_type);
-}
-
static inline bool ovsdb_type_is_scalar(const struct ovsdb_type *type)
{
- return (type->value_type == OVSDB_TYPE_VOID
+ return (type->value.type == OVSDB_TYPE_VOID
&& type->n_min == 1 && type->n_max == 1);
}
@@ -123,13 +175,13 @@ static inline bool ovsdb_type_is_composite(const struct ovsdb_type *type)
static inline bool ovsdb_type_is_set(const struct ovsdb_type *type)
{
- return (type->value_type == OVSDB_TYPE_VOID
+ return (type->value.type == OVSDB_TYPE_VOID
&& (type->n_min != 1 || type->n_max != 1));
}
static inline bool ovsdb_type_is_map(const struct ovsdb_type *type)
{
- return type->value_type != OVSDB_TYPE_VOID;
+ return type->value.type != OVSDB_TYPE_VOID;
}
#endif /* ovsdb-types.h */
diff --git a/lib/pcre.h b/lib/pcre.h
new file mode 100644
index 000000000..5ade83319
--- /dev/null
+++ b/lib/pcre.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2010 Nicira Networks.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef PCRE_H
+#define PCRE_H 1
+
+#ifdef HAVE_PCRE
+#include_next <pcre.h>
+#else
+typedef void pcre;
+#endif
+
+#endif /* pcre.h */
diff --git a/lib/unicode.c b/lib/unicode.c
index 69ebcfc9d..e8fea8663 100644
--- a/lib/unicode.c
+++ b/lib/unicode.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2009 Nicira Networks.
+ * Copyright (c) 2009, 2010 Nicira Networks.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,6 +18,11 @@
#include "unicode.h"
+#include <inttypes.h>
+
+#include "dynamic-string.h"
+#include "util.h"
+
/* Returns the unicode code point corresponding to leading surrogate 'leading'
* and trailing surrogate 'trailing'. The return value will not make any
* sense if 'leading' or 'trailing' are not in the correct ranges for leading
@@ -36,3 +41,124 @@ utf16_decode_surrogate_pair(int leading, int trailing)
int x1 = trailing & 0x3ff;
return (u << 16) | (x0 << 10) | x1;
}
+
+/* Returns the number of Unicode characters in UTF-8 string 's'. */
+size_t
+utf8_length(const char *s_)
+{
+ const uint8_t *s;
+ size_t length;
+
+ length = 0;
+ for (s = (const uint8_t *) s_; *s != '\0'; s++) {
+ /* The most-significant bits of the first byte in a character are one
+ * of 2#01, 2#00, or 2#11. 2#10 is a continuation byte. */
+ length += (*s & 0xc0) != 0x80;
+ }
+ return length;
+}
+
+static char *
+invalid_utf8_sequence(const uint8_t *s, int n, size_t *lengthp)
+{
+ struct ds msg;
+ int i;
+
+ if (lengthp) {
+ *lengthp = 0;
+ }
+
+ ds_init(&msg);
+ ds_put_cstr(&msg, "invalid UTF-8 sequence");
+ for (i = 0; i < n; i++) {
+ ds_put_format(&msg, " 0x%02"PRIx8, s[i]);
+ }
+ return ds_steal_cstr(&msg);
+}
+
+struct utf8_sequence {
+ uint8_t octets[5][2];
+};
+
+static const struct utf8_sequence *
+lookup_utf8_sequence(uint8_t c)
+{
+ static const struct utf8_sequence seqs[] = {
+ { { { 0x01, 0x7f },
+ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } } },
+
+ { { { 0xc2, 0xdf }, { 0x80, 0xbf },
+ { 0, 0 }, { 0, 0 }, { 0, 0 } } },
+
+ { { { 0xe0, 0xe0 }, { 0xa0, 0xbf }, { 0x80, 0xbf },
+ {0,0}, {0, 0 } } },
+
+ { { { 0xe1, 0xec }, { 0x80, 0xbf }, { 0x80, 0xbf },
+ { 0, 0 }, { 0, 0 } } },
+
+ { { { 0xed, 0xed }, { 0x80, 0x9f }, { 0x80, 0xbf },
+ { 0, 0 }, { 0, 0 } } },
+
+ { { { 0xee, 0xef }, { 0x80, 0xbf }, { 0x80, 0xbf },
+ { 0, 0 }, { 0, 0 } } },
+
+ { { { 0xf0, 0xf0 }, { 0x90, 0xbf }, { 0x80, 0xbf }, { 0x80, 0xbf },
+ { 0, 0 } } },
+
+ { { { 0xf1, 0xf3 }, { 0x80, 0xbf }, { 0x80, 0xbf }, { 0x80, 0xbf },
+ { 0, 0 } } },
+
+ { { { 0xf4, 0xf4 }, { 0x80, 0x8f }, { 0x80, 0xbf }, { 0x80, 0xbf },
+ { 0, 0 } } },
+ };
+
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(seqs); i++) {
+ const uint8_t *o = seqs[i].octets[0];
+ if (c >= o[0] && c <= o[1]) {
+ return &seqs[i];
+ }
+ }
+ return NULL;
+}
+
+/* Checks that 's' is a valid, null-terminated UTF-8 string. If so, returns a
+ * null pointer and sets '*lengthp' to the number of Unicode characters in
+ * 's'. If not, returns an error message that the caller must free and sets
+ * '*lengthp' to 0.
+ *
+ * 'lengthp' may be NULL if the length is not needed. */
+char *
+utf8_validate(const char *s_, size_t *lengthp)
+{
+ size_t length = 0;
+ const uint8_t *s;
+
+ for (s = (const uint8_t *) s_; *s != '\0'; ) {
+ length++;
+ if (s[0] < 0x80) {
+ s++;
+ } else {
+ const struct utf8_sequence *seq;
+ int i;
+
+ seq = lookup_utf8_sequence(s[0]);
+ if (!seq) {
+ return invalid_utf8_sequence(s, 1, lengthp);
+ }
+
+ for (i = 1; seq->octets[i][0]; i++) {
+ const uint8_t *o = seq->octets[i];
+ if (s[i] < o[0] || s[i] > o[1]) {
+ return invalid_utf8_sequence(s, i + 1, lengthp);
+ }
+ }
+ s += i;
+ }
+ }
+ if (lengthp) {
+ *lengthp = length;
+ }
+ return NULL;
+}
diff --git a/lib/unicode.h b/lib/unicode.h
index 0f20bdc78..b2078e6dc 100644
--- a/lib/unicode.h
+++ b/lib/unicode.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2009 Nicira Networks.
+ * Copyright (c) 2009, 2010 Nicira Networks.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,6 +18,8 @@
#define UNICODE_H 1
#include <stdbool.h>
+#include <stddef.h>
+#include "compiler.h"
/* Returns true if 'c' is a Unicode code point, otherwise false. */
static inline bool
@@ -50,4 +52,7 @@ uc_is_surrogate(int c)
int utf16_decode_surrogate_pair(int leading, int trailing);
+size_t utf8_length(const char *);
+char *utf8_validate(const char *, size_t *lengthp) WARN_UNUSED_RESULT;
+
#endif /* unicode.h */