]> git.proxmox.com Git - mirror_ovs.git/blob - lib/ovsdb-data.c
ovsdb: Implement new "declare" operation.
[mirror_ovs.git] / lib / ovsdb-data.c
1 /* Copyright (c) 2009 Nicira Networks
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at:
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include <config.h>
17
18 #include "ovsdb-data.h"
19
20 #include <assert.h>
21
22 #include "hash.h"
23 #include "ovsdb-error.h"
24 #include "json.h"
25 #include "shash.h"
26 #include "sort.h"
27
28 static struct json *
29 wrap_json(const char *name, struct json *wrapped)
30 {
31 return json_array_create_2(json_string_create(name), wrapped);
32 }
33
34 void
35 ovsdb_atom_init_default(union ovsdb_atom *atom, enum ovsdb_atomic_type type)
36 {
37 switch (type) {
38 case OVSDB_TYPE_VOID:
39 NOT_REACHED();
40
41 case OVSDB_TYPE_INTEGER:
42 atom->integer = 0;
43 break;
44
45 case OVSDB_TYPE_REAL:
46 atom->real = 0.0;
47 break;
48
49 case OVSDB_TYPE_BOOLEAN:
50 atom->boolean = false;
51 break;
52
53 case OVSDB_TYPE_STRING:
54 atom->string = xmemdup("", 1);
55 break;
56
57 case OVSDB_TYPE_UUID:
58 uuid_zero(&atom->uuid);
59 break;
60
61 case OVSDB_N_TYPES:
62 default:
63 NOT_REACHED();
64 }
65 }
66
67 void
68 ovsdb_atom_clone(union ovsdb_atom *new, const union ovsdb_atom *old,
69 enum ovsdb_atomic_type type)
70 {
71 switch (type) {
72 case OVSDB_TYPE_VOID:
73 NOT_REACHED();
74
75 case OVSDB_TYPE_INTEGER:
76 new->integer = old->integer;
77 break;
78
79 case OVSDB_TYPE_REAL:
80 new->real = old->real;
81 break;
82
83 case OVSDB_TYPE_BOOLEAN:
84 new->boolean = old->boolean;
85 break;
86
87 case OVSDB_TYPE_STRING:
88 new->string = xstrdup(old->string);
89 break;
90
91 case OVSDB_TYPE_UUID:
92 new->uuid = old->uuid;
93 break;
94
95 case OVSDB_N_TYPES:
96 default:
97 NOT_REACHED();
98 }
99 }
100
101 void
102 ovsdb_atom_swap(union ovsdb_atom *a, union ovsdb_atom *b)
103 {
104 union ovsdb_atom tmp = *a;
105 *a = *b;
106 *b = tmp;
107 }
108
109 uint32_t
110 ovsdb_atom_hash(const union ovsdb_atom *atom, enum ovsdb_atomic_type type,
111 uint32_t basis)
112 {
113 switch (type) {
114 case OVSDB_TYPE_VOID:
115 NOT_REACHED();
116
117 case OVSDB_TYPE_INTEGER:
118 return hash_int(atom->integer, basis);
119
120 case OVSDB_TYPE_REAL:
121 return hash_double(atom->real, basis);
122
123 case OVSDB_TYPE_BOOLEAN:
124 return hash_boolean(atom->boolean, basis);
125
126 case OVSDB_TYPE_STRING:
127 return hash_string(atom->string, basis);
128
129 case OVSDB_TYPE_UUID:
130 return hash_int(uuid_hash(&atom->uuid), basis);
131
132 case OVSDB_N_TYPES:
133 default:
134 NOT_REACHED();
135 }
136 }
137
138 int
139 ovsdb_atom_compare_3way(const union ovsdb_atom *a,
140 const union ovsdb_atom *b,
141 enum ovsdb_atomic_type type)
142 {
143 switch (type) {
144 case OVSDB_TYPE_VOID:
145 NOT_REACHED();
146
147 case OVSDB_TYPE_INTEGER:
148 return a->integer < b->integer ? -1 : a->integer > b->integer;
149
150 case OVSDB_TYPE_REAL:
151 return a->real < b->real ? -1 : a->real > b->real;
152
153 case OVSDB_TYPE_BOOLEAN:
154 return a->boolean - b->boolean;
155
156 case OVSDB_TYPE_STRING:
157 return strcmp(a->string, b->string);
158
159 case OVSDB_TYPE_UUID:
160 return uuid_compare_3way(&a->uuid, &b->uuid);
161
162 case OVSDB_N_TYPES:
163 default:
164 NOT_REACHED();
165 }
166 }
167
168 static struct ovsdb_error *
169 unwrap_json(const struct json *json, const char *name,
170 enum json_type value_type, const struct json **value)
171 {
172 if (json->type != JSON_ARRAY
173 || json->u.array.n != 2
174 || json->u.array.elems[0]->type != JSON_STRING
175 || (name && strcmp(json->u.array.elems[0]->u.string, name))
176 || json->u.array.elems[1]->type != value_type)
177 {
178 return ovsdb_syntax_error(json, NULL, "expected [\"%s\", <%s>]", name,
179 json_type_to_string(value_type));
180 }
181 *value = json->u.array.elems[1];
182 return NULL;
183 }
184
185 static struct ovsdb_error *
186 parse_json_pair(const struct json *json,
187 const struct json **elem0, const struct json **elem1)
188 {
189 if (json->type != JSON_ARRAY || json->u.array.n != 2) {
190 return ovsdb_syntax_error(json, NULL, "expected 2-element array");
191 }
192 *elem0 = json->u.array.elems[0];
193 *elem1 = json->u.array.elems[1];
194 return NULL;
195 }
196
197 static struct ovsdb_error *
198 ovsdb_atom_parse_uuid(struct uuid *uuid, const struct json *json,
199 const struct ovsdb_symbol_table *symtab)
200 WARN_UNUSED_RESULT;
201
202 static struct ovsdb_error *
203 ovsdb_atom_parse_uuid(struct uuid *uuid, const struct json *json,
204 const struct ovsdb_symbol_table *symtab)
205 {
206 struct ovsdb_error *error0;
207 const struct json *value;
208
209 error0 = unwrap_json(json, "uuid", JSON_STRING, &value);
210 if (!error0) {
211 const char *uuid_string = json_string(value);
212 if (!uuid_from_string(uuid, uuid_string)) {
213 return ovsdb_syntax_error(json, NULL, "\"%s\" is not a valid UUID",
214 uuid_string);
215 }
216 } else if (symtab) {
217 struct ovsdb_error *error1;
218
219 error1 = unwrap_json(json, "named-uuid", JSON_STRING, &value);
220 if (!error1) {
221 const char *name = json_string(value);
222 const struct ovsdb_symbol *symbol;
223
224 ovsdb_error_destroy(error0);
225
226 symbol = ovsdb_symbol_table_get(symtab, name);
227 if (symbol) {
228 *uuid = symbol->uuid;
229 return NULL;
230 } else {
231 return ovsdb_syntax_error(json, NULL,
232 "unknown named-uuid \"%s\"", name);
233 }
234 }
235 ovsdb_error_destroy(error1);
236 }
237
238 return error0;
239 }
240
241 struct ovsdb_error *
242 ovsdb_atom_from_json(union ovsdb_atom *atom, enum ovsdb_atomic_type type,
243 const struct json *json,
244 const struct ovsdb_symbol_table *symtab)
245 {
246 switch (type) {
247 case OVSDB_TYPE_VOID:
248 NOT_REACHED();
249
250 case OVSDB_TYPE_INTEGER:
251 if (json->type == JSON_INTEGER) {
252 atom->integer = json->u.integer;
253 return NULL;
254 }
255 break;
256
257 case OVSDB_TYPE_REAL:
258 if (json->type == JSON_INTEGER) {
259 atom->real = json->u.integer;
260 return NULL;
261 } else if (json->type == JSON_REAL) {
262 atom->real = json->u.real;
263 return NULL;
264 }
265 break;
266
267 case OVSDB_TYPE_BOOLEAN:
268 if (json->type == JSON_TRUE) {
269 atom->boolean = true;
270 return NULL;
271 } else if (json->type == JSON_FALSE) {
272 atom->boolean = false;
273 return NULL;
274 }
275 break;
276
277 case OVSDB_TYPE_STRING:
278 if (json->type == JSON_STRING) {
279 atom->string = xstrdup(json->u.string);
280 return NULL;
281 }
282 break;
283
284 case OVSDB_TYPE_UUID:
285 return ovsdb_atom_parse_uuid(&atom->uuid, json, symtab);
286
287 case OVSDB_N_TYPES:
288 default:
289 NOT_REACHED();
290 }
291
292 return ovsdb_syntax_error(json, NULL, "expected %s",
293 ovsdb_atomic_type_to_string(type));
294 }
295
296 struct json *
297 ovsdb_atom_to_json(const union ovsdb_atom *atom, enum ovsdb_atomic_type type)
298 {
299 switch (type) {
300 case OVSDB_TYPE_VOID:
301 NOT_REACHED();
302
303 case OVSDB_TYPE_INTEGER:
304 return json_integer_create(atom->integer);
305
306 case OVSDB_TYPE_REAL:
307 return json_real_create(atom->real);
308
309 case OVSDB_TYPE_BOOLEAN:
310 return json_boolean_create(atom->boolean);
311
312 case OVSDB_TYPE_STRING:
313 return json_string_create(atom->string);
314
315 case OVSDB_TYPE_UUID:
316 return wrap_json("uuid", json_string_create_nocopy(
317 xasprintf(UUID_FMT, UUID_ARGS(&atom->uuid))));
318
319 case OVSDB_N_TYPES:
320 default:
321 NOT_REACHED();
322 }
323 }
324 \f
325 static union ovsdb_atom *
326 alloc_default_atoms(enum ovsdb_atomic_type type, size_t n)
327 {
328 if (type != OVSDB_TYPE_VOID && n) {
329 union ovsdb_atom *atoms;
330 unsigned int i;
331
332 atoms = xmalloc(n * sizeof *atoms);
333 for (i = 0; i < n; i++) {
334 ovsdb_atom_init_default(&atoms[i], type);
335 }
336 return atoms;
337 } else {
338 /* Avoid wasting memory in the n == 0 case, because xmalloc(0) is
339 * treated as xmalloc(1). */
340 return NULL;
341 }
342 }
343
344 void
345 ovsdb_datum_init_default(struct ovsdb_datum *datum,
346 const struct ovsdb_type *type)
347 {
348 datum->n = type->n_min;
349 datum->keys = alloc_default_atoms(type->key_type, datum->n);
350 datum->values = alloc_default_atoms(type->value_type, datum->n);
351 }
352
353 static union ovsdb_atom *
354 clone_atoms(const union ovsdb_atom *old, enum ovsdb_atomic_type type, size_t n)
355 {
356 if (type != OVSDB_TYPE_VOID && n) {
357 union ovsdb_atom *new;
358 unsigned int i;
359
360 new = xmalloc(n * sizeof *new);
361 for (i = 0; i < n; i++) {
362 ovsdb_atom_clone(&new[i], &old[i], type);
363 }
364 return new;
365 } else {
366 /* Avoid wasting memory in the n == 0 case, because xmalloc(0) is
367 * treated as xmalloc(1). */
368 return NULL;
369 }
370 }
371
372 void
373 ovsdb_datum_clone(struct ovsdb_datum *new, const struct ovsdb_datum *old,
374 const struct ovsdb_type *type)
375 {
376 unsigned int n = old->n;
377 new->n = n;
378 new->keys = clone_atoms(old->keys, type->key_type, n);
379 new->values = clone_atoms(old->values, type->value_type, n);
380 }
381
382 static void
383 free_data(enum ovsdb_atomic_type type,
384 union ovsdb_atom *atoms, size_t n_atoms)
385 {
386 if (ovsdb_atom_needs_destruction(type)) {
387 unsigned int i;
388 for (i = 0; i < n_atoms; i++) {
389 ovsdb_atom_destroy(&atoms[i], type);
390 }
391 }
392 free(atoms);
393 }
394
395 void
396 ovsdb_datum_destroy(struct ovsdb_datum *datum, const struct ovsdb_type *type)
397 {
398 free_data(type->key_type, datum->keys, datum->n);
399 free_data(type->value_type, datum->values, datum->n);
400 }
401
402 void
403 ovsdb_datum_swap(struct ovsdb_datum *a, struct ovsdb_datum *b)
404 {
405 struct ovsdb_datum tmp = *a;
406 *a = *b;
407 *b = tmp;
408 }
409
410 struct ovsdb_datum_sort_cbdata {
411 const struct ovsdb_type *type;
412 struct ovsdb_datum *datum;
413 };
414
415 static int
416 ovsdb_datum_sort_compare_cb(size_t a, size_t b, void *cbdata_)
417 {
418 struct ovsdb_datum_sort_cbdata *cbdata = cbdata_;
419
420 return ovsdb_atom_compare_3way(&cbdata->datum->keys[a],
421 &cbdata->datum->keys[b],
422 cbdata->type->key_type);
423 }
424
425 static void
426 ovsdb_datum_sort_swap_cb(size_t a, size_t b, void *cbdata_)
427 {
428 struct ovsdb_datum_sort_cbdata *cbdata = cbdata_;
429
430 ovsdb_atom_swap(&cbdata->datum->keys[a], &cbdata->datum->keys[b]);
431 if (cbdata->type->value_type != OVSDB_TYPE_VOID) {
432 ovsdb_atom_swap(&cbdata->datum->values[a], &cbdata->datum->values[b]);
433 }
434 }
435
436 static struct ovsdb_error *
437 ovsdb_datum_sort(struct ovsdb_datum *datum, const struct ovsdb_type *type)
438 {
439 if (datum->n < 2) {
440 return NULL;
441 } else {
442 struct ovsdb_datum_sort_cbdata cbdata;
443 size_t i;
444
445 cbdata.type = type;
446 cbdata.datum = datum;
447 sort(datum->n, ovsdb_datum_sort_compare_cb, ovsdb_datum_sort_swap_cb,
448 &cbdata);
449
450 for (i = 0; i < datum->n - 1; i++) {
451 if (ovsdb_atom_equals(&datum->keys[i], &datum->keys[i + 1],
452 type->key_type)) {
453 if (ovsdb_type_is_map(type)) {
454 return ovsdb_error(NULL, "map contains duplicate key");
455 } else {
456 return ovsdb_error(NULL, "set contains duplicate");
457 }
458 }
459 }
460
461 return NULL;
462 }
463 }
464
465 struct ovsdb_error *
466 ovsdb_datum_from_json(struct ovsdb_datum *datum,
467 const struct ovsdb_type *type,
468 const struct json *json,
469 const struct ovsdb_symbol_table *symtab)
470 {
471 struct ovsdb_error *error;
472
473 if (ovsdb_type_is_scalar(type)) {
474 datum->n = 1;
475 datum->keys = xmalloc(sizeof *datum->keys);
476 datum->values = NULL;
477
478 error = ovsdb_atom_from_json(&datum->keys[0], type->key_type,
479 json, symtab);
480 if (error) {
481 free(datum->keys);
482 }
483 return error;
484 } else {
485 bool is_map = ovsdb_type_is_map(type);
486 const char *class = is_map ? "map" : "set";
487 const struct json *inner;
488 unsigned int i;
489 size_t n;
490
491 assert(is_map || ovsdb_type_is_set(type));
492
493 error = unwrap_json(json, class, JSON_ARRAY, &inner);
494 if (error) {
495 return error;
496 }
497
498 n = inner->u.array.n;
499 if (n < type->n_min || n > type->n_max) {
500 return ovsdb_syntax_error(json, NULL, "%s must have %u to "
501 "%u members but %zu are present",
502 class, type->n_min, type->n_max, n);
503 }
504
505 datum->n = 0;
506 datum->keys = xmalloc(n * sizeof *datum->keys);
507 datum->values = is_map ? xmalloc(n * sizeof *datum->values) : NULL;
508 for (i = 0; i < n; i++) {
509 const struct json *element = inner->u.array.elems[i];
510 const struct json *key = NULL;
511 const struct json *value = NULL;
512
513 if (!is_map) {
514 key = element;
515 } else {
516 error = parse_json_pair(element, &key, &value);
517 if (error) {
518 goto error;
519 }
520 }
521
522 error = ovsdb_atom_from_json(&datum->keys[i], type->key_type,
523 key, symtab);
524 if (error) {
525 goto error;
526 }
527
528 if (is_map) {
529 error = ovsdb_atom_from_json(&datum->values[i],
530 type->value_type, value, symtab);
531 if (error) {
532 ovsdb_atom_destroy(&datum->keys[i], type->key_type);
533 goto error;
534 }
535 }
536
537 datum->n++;
538 }
539
540 error = ovsdb_datum_sort(datum, type);
541 if (error) {
542 goto error;
543 }
544
545 return NULL;
546
547 error:
548 ovsdb_datum_destroy(datum, type);
549 return error;
550 }
551 }
552
553 struct json *
554 ovsdb_datum_to_json(const struct ovsdb_datum *datum,
555 const struct ovsdb_type *type)
556 {
557 /* These tests somewhat tolerate a 'datum' that does not exactly match
558 * 'type', in particular a datum with 'n' not in the allowed range. */
559 if (datum->n == 1 && ovsdb_type_is_scalar(type)) {
560 return ovsdb_atom_to_json(&datum->keys[0], type->key_type);
561 } else if (type->value_type == OVSDB_TYPE_VOID) {
562 struct json **elems;
563 size_t i;
564
565 elems = xmalloc(datum->n * sizeof *elems);
566 for (i = 0; i < datum->n; i++) {
567 elems[i] = ovsdb_atom_to_json(&datum->keys[i], type->key_type);
568 }
569
570 return wrap_json("set", json_array_create(elems, datum->n));
571 } else {
572 struct json **elems;
573 size_t i;
574
575 elems = xmalloc(datum->n * sizeof *elems);
576 for (i = 0; i < datum->n; i++) {
577 elems[i] = json_array_create_2(
578 ovsdb_atom_to_json(&datum->keys[i], type->key_type),
579 ovsdb_atom_to_json(&datum->values[i], type->value_type));
580 }
581
582 return wrap_json("map", json_array_create(elems, datum->n));
583 }
584 }
585
586 static uint32_t
587 hash_atoms(enum ovsdb_atomic_type type, const union ovsdb_atom *atoms,
588 unsigned int n, uint32_t basis)
589 {
590 if (type != OVSDB_TYPE_VOID) {
591 unsigned int i;
592
593 for (i = 0; i < n; i++) {
594 basis = ovsdb_atom_hash(&atoms[i], type, basis);
595 }
596 }
597 return basis;
598 }
599
600 uint32_t
601 ovsdb_datum_hash(const struct ovsdb_datum *datum,
602 const struct ovsdb_type *type, uint32_t basis)
603 {
604 basis = hash_atoms(type->key_type, datum->keys, datum->n, basis);
605 basis ^= (type->key_type << 24) | (type->value_type << 16) | datum->n;
606 basis = hash_atoms(type->value_type, datum->values, datum->n, basis);
607 return basis;
608 }
609
610 static int
611 atom_arrays_compare_3way(const union ovsdb_atom *a,
612 const union ovsdb_atom *b,
613 enum ovsdb_atomic_type type,
614 size_t n)
615 {
616 unsigned int i;
617
618 for (i = 0; i < n; i++) {
619 int cmp = ovsdb_atom_compare_3way(&a[i], &b[i], type);
620 if (cmp) {
621 return cmp;
622 }
623 }
624
625 return 0;
626 }
627
628 bool
629 ovsdb_datum_equals(const struct ovsdb_datum *a,
630 const struct ovsdb_datum *b,
631 const struct ovsdb_type *type)
632 {
633 return !ovsdb_datum_compare_3way(a, b, type);
634 }
635
636 int
637 ovsdb_datum_compare_3way(const struct ovsdb_datum *a,
638 const struct ovsdb_datum *b,
639 const struct ovsdb_type *type)
640 {
641 int cmp;
642
643 if (a->n != b->n) {
644 return a->n < b->n ? -1 : 1;
645 }
646
647 cmp = atom_arrays_compare_3way(a->keys, b->keys, type->key_type, a->n);
648 if (cmp) {
649 return cmp;
650 }
651
652 return (type->value_type == OVSDB_TYPE_VOID ? 0
653 : atom_arrays_compare_3way(a->values, b->values, type->value_type,
654 a->n));
655 }
656
657 static bool
658 ovsdb_datum_contains(const struct ovsdb_datum *a, int i,
659 const struct ovsdb_datum *b,
660 const struct ovsdb_type *type)
661 {
662 int low = 0;
663 int high = b->n;
664 while (low < high) {
665 int j = (low + high) / 2;
666 int cmp = ovsdb_atom_compare_3way(&a->keys[i], &b->keys[j], type->key_type);
667 if (cmp < 0) {
668 high = j;
669 } else if (cmp > 0) {
670 low = j + 1;
671 } else {
672 return (type->value_type == OVSDB_TYPE_VOID
673 || ovsdb_atom_equals(&a->values[i], &b->values[j],
674 type->value_type));
675 }
676 }
677 return false;
678 }
679
680 /* Returns true if every element in 'a' is also in 'b', false otherwise. */
681 bool
682 ovsdb_datum_includes_all(const struct ovsdb_datum *a,
683 const struct ovsdb_datum *b,
684 const struct ovsdb_type *type)
685 {
686 size_t i;
687
688 for (i = 0; i < a->n; i++) {
689 if (!ovsdb_datum_contains(a, i, b, type)) {
690 return false;
691 }
692 }
693 return true;
694 }
695
696 /* Returns true if no element in 'a' is also in 'b', false otherwise. */
697 bool
698 ovsdb_datum_excludes_all(const struct ovsdb_datum *a,
699 const struct ovsdb_datum *b,
700 const struct ovsdb_type *type)
701 {
702 size_t i;
703
704 for (i = 0; i < a->n; i++) {
705 if (ovsdb_datum_contains(a, i, b, type)) {
706 return false;
707 }
708 }
709 return true;
710 }
711 \f
712 struct ovsdb_symbol_table {
713 struct shash sh;
714 };
715
716 struct ovsdb_symbol_table *
717 ovsdb_symbol_table_create(void)
718 {
719 struct ovsdb_symbol_table *symtab = xmalloc(sizeof *symtab);
720 shash_init(&symtab->sh);
721 return symtab;
722 }
723
724 void
725 ovsdb_symbol_table_destroy(struct ovsdb_symbol_table *symtab)
726 {
727 if (symtab) {
728 struct shash_node *node, *next;
729
730 SHASH_FOR_EACH_SAFE (node, next, &symtab->sh) {
731 struct ovsdb_symbol *symbol = node->data;
732 free(symbol);
733 shash_delete(&symtab->sh, node);
734 }
735 shash_destroy(&symtab->sh);
736 free(symtab);
737 }
738 }
739
740 struct ovsdb_symbol *
741 ovsdb_symbol_table_get(const struct ovsdb_symbol_table *symtab,
742 const char *name)
743 {
744 return shash_find_data(&symtab->sh, name);
745 }
746
747 void
748 ovsdb_symbol_table_put(struct ovsdb_symbol_table *symtab, const char *name,
749 const struct uuid *uuid, bool used)
750 {
751 struct ovsdb_symbol *symbol;
752
753 assert(!ovsdb_symbol_table_get(symtab, name));
754 symbol = xmalloc(sizeof *symbol);
755 symbol->uuid = *uuid;
756 symbol->used = used;
757 shash_add(&symtab->sh, name, symbol);
758 }