diff options
author | Ben Pfaff <blp@nicira.com> | 2010-02-08 14:09:36 -0800 |
---|---|---|
committer | Ben Pfaff <blp@nicira.com> | 2010-02-08 14:16:19 -0800 |
commit | bd76d25d8b3b7d11c5a326e91d784ad2cdeecd45 (patch) | |
tree | 688e7370f9794e3c4189c38ff38a59937c9016e1 /lib | |
parent | 629cd2f17fedf8d922f61ffd13365d1f4f9b34fe (diff) | |
download | openvswitch-bd76d25d8b3b7d11c5a326e91d784ad2cdeecd45.tar.gz |
ovsdb: Add simple constraints.
Diffstat (limited to 'lib')
-rw-r--r-- | lib/automake.mk | 1 | ||||
-rw-r--r-- | lib/ovsdb-data.c | 383 | ||||
-rw-r--r-- | lib/ovsdb-data.h | 13 | ||||
-rw-r--r-- | lib/ovsdb-idl.c | 4 | ||||
-rw-r--r-- | lib/ovsdb-parser.h | 3 | ||||
-rw-r--r-- | lib/ovsdb-types.c | 471 | ||||
-rw-r--r-- | lib/ovsdb-types.h | 82 | ||||
-rw-r--r-- | lib/pcre.h | 26 | ||||
-rw-r--r-- | lib/unicode.c | 128 | ||||
-rw-r--r-- | lib/unicode.h | 7 |
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 */ |