]>
Commit | Line | Data |
---|---|---|
d2d3264c | 1 | /* Copyright (c) 2009, 2010, 2011, 2013 Nicira, Inc. |
f85f8ebb BP |
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-types.h" | |
19 | ||
bd76d25d | 20 | #include <float.h> |
f85f8ebb BP |
21 | #include <limits.h> |
22 | ||
3e8a2ad1 | 23 | #include "openvswitch/dynamic-string.h" |
ee89ea7b | 24 | #include "openvswitch/json.h" |
d2d3264c | 25 | #include "ovs-thread.h" |
bfc96d9b | 26 | #include "ovsdb-data.h" |
f85f8ebb BP |
27 | #include "ovsdb-error.h" |
28 | #include "ovsdb-parser.h" | |
ee89ea7b | 29 | #include "util.h" |
f85f8ebb BP |
30 | |
31 | const struct ovsdb_type ovsdb_type_integer = | |
bd76d25d | 32 | OVSDB_TYPE_SCALAR_INITIALIZER(OVSDB_BASE_INTEGER_INIT); |
f85f8ebb | 33 | const struct ovsdb_type ovsdb_type_real = |
bd76d25d | 34 | OVSDB_TYPE_SCALAR_INITIALIZER(OVSDB_BASE_REAL_INIT); |
f85f8ebb | 35 | const struct ovsdb_type ovsdb_type_boolean = |
bd76d25d | 36 | OVSDB_TYPE_SCALAR_INITIALIZER(OVSDB_BASE_BOOLEAN_INIT); |
f85f8ebb | 37 | const struct ovsdb_type ovsdb_type_string = |
bd76d25d | 38 | OVSDB_TYPE_SCALAR_INITIALIZER(OVSDB_BASE_STRING_INIT); |
f85f8ebb | 39 | const struct ovsdb_type ovsdb_type_uuid = |
bd76d25d BP |
40 | OVSDB_TYPE_SCALAR_INITIALIZER(OVSDB_BASE_UUID_INIT); |
41 | \f | |
42 | /* ovsdb_atomic_type */ | |
f85f8ebb BP |
43 | const char * |
44 | ovsdb_atomic_type_to_string(enum ovsdb_atomic_type type) | |
45 | { | |
46 | switch (type) { | |
47 | case OVSDB_TYPE_VOID: | |
48 | return "void"; | |
49 | ||
50 | case OVSDB_TYPE_INTEGER: | |
51 | return "integer"; | |
52 | ||
53 | case OVSDB_TYPE_REAL: | |
54 | return "real"; | |
55 | ||
56 | case OVSDB_TYPE_BOOLEAN: | |
57 | return "boolean"; | |
58 | ||
59 | case OVSDB_TYPE_STRING: | |
60 | return "string"; | |
61 | ||
62 | case OVSDB_TYPE_UUID: | |
63 | return "uuid"; | |
64 | ||
65 | case OVSDB_N_TYPES: | |
66 | default: | |
67 | return "<invalid>"; | |
68 | } | |
69 | } | |
70 | ||
71 | struct json * | |
72 | ovsdb_atomic_type_to_json(enum ovsdb_atomic_type type) | |
73 | { | |
74 | return json_string_create(ovsdb_atomic_type_to_string(type)); | |
75 | } | |
76 | ||
f85f8ebb BP |
77 | bool |
78 | ovsdb_atomic_type_from_string(const char *string, enum ovsdb_atomic_type *type) | |
79 | { | |
80 | if (!strcmp(string, "integer")) { | |
81 | *type = OVSDB_TYPE_INTEGER; | |
82 | } else if (!strcmp(string, "real")) { | |
83 | *type = OVSDB_TYPE_REAL; | |
84 | } else if (!strcmp(string, "boolean")) { | |
85 | *type = OVSDB_TYPE_BOOLEAN; | |
86 | } else if (!strcmp(string, "string")) { | |
87 | *type = OVSDB_TYPE_STRING; | |
88 | } else if (!strcmp(string, "uuid")) { | |
89 | *type = OVSDB_TYPE_UUID; | |
90 | } else { | |
91 | return false; | |
92 | } | |
93 | return true; | |
94 | } | |
95 | ||
96 | struct ovsdb_error * | |
97 | ovsdb_atomic_type_from_json(enum ovsdb_atomic_type *type, | |
98 | const struct json *json) | |
99 | { | |
100 | if (json->type == JSON_STRING) { | |
101 | if (ovsdb_atomic_type_from_string(json_string(json), type)) { | |
102 | return NULL; | |
103 | } else { | |
104 | *type = OVSDB_TYPE_VOID; | |
105 | return ovsdb_syntax_error(json, NULL, | |
106 | "\"%s\" is not an atomic-type", | |
107 | json_string(json)); | |
108 | } | |
109 | } else { | |
110 | *type = OVSDB_TYPE_VOID; | |
111 | return ovsdb_syntax_error(json, NULL, "atomic-type expected"); | |
112 | } | |
113 | } | |
bd76d25d BP |
114 | \f |
115 | /* ovsdb_base_type */ | |
116 | ||
117 | void | |
118 | ovsdb_base_type_init(struct ovsdb_base_type *base, enum ovsdb_atomic_type type) | |
119 | { | |
120 | base->type = type; | |
bfc96d9b | 121 | base->enum_ = NULL; |
bd76d25d BP |
122 | |
123 | switch (base->type) { | |
124 | case OVSDB_TYPE_VOID: | |
125 | break; | |
126 | ||
127 | case OVSDB_TYPE_INTEGER: | |
128 | base->u.integer.min = INT64_MIN; | |
129 | base->u.integer.max = INT64_MAX; | |
130 | break; | |
131 | ||
132 | case OVSDB_TYPE_REAL: | |
133 | base->u.real.min = -DBL_MAX; | |
134 | base->u.real.max = DBL_MAX; | |
135 | break; | |
136 | ||
137 | case OVSDB_TYPE_BOOLEAN: | |
138 | break; | |
139 | ||
140 | case OVSDB_TYPE_STRING: | |
bd76d25d BP |
141 | base->u.string.minLen = 0; |
142 | base->u.string.maxLen = UINT_MAX; | |
143 | break; | |
144 | ||
145 | case OVSDB_TYPE_UUID: | |
0d0f05b9 BP |
146 | base->u.uuid.refTableName = NULL; |
147 | base->u.uuid.refTable = NULL; | |
bd76d25d BP |
148 | break; |
149 | ||
150 | case OVSDB_N_TYPES: | |
428b2edd | 151 | OVS_NOT_REACHED(); |
bd76d25d BP |
152 | |
153 | default: | |
428b2edd | 154 | OVS_NOT_REACHED(); |
bd76d25d BP |
155 | } |
156 | } | |
157 | ||
bfc96d9b BP |
158 | /* Returns the type of the 'enum_' member for an ovsdb_base_type whose 'type' |
159 | * is 'atomic_type'. */ | |
160 | const struct ovsdb_type * | |
161 | ovsdb_base_type_get_enum_type(enum ovsdb_atomic_type atomic_type) | |
162 | { | |
d2d3264c | 163 | static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; |
bfc96d9b BP |
164 | static struct ovsdb_type *types[OVSDB_N_TYPES]; |
165 | ||
d2d3264c BP |
166 | if (ovsthread_once_start(&once)) { |
167 | enum ovsdb_atomic_type i; | |
bfc96d9b | 168 | |
d2d3264c BP |
169 | for (i = 0; i < OVSDB_N_TYPES; i++) { |
170 | struct ovsdb_type *type; | |
171 | ||
172 | types[i] = type = xmalloc(sizeof *type); | |
173 | ovsdb_base_type_init(&type->key, i); | |
174 | ovsdb_base_type_init(&type->value, OVSDB_TYPE_VOID); | |
175 | type->n_min = 1; | |
176 | type->n_max = UINT_MAX; | |
177 | } | |
178 | ||
179 | ovsthread_once_done(&once); | |
bfc96d9b BP |
180 | } |
181 | return types[atomic_type]; | |
182 | } | |
183 | ||
bd76d25d BP |
184 | void |
185 | ovsdb_base_type_clone(struct ovsdb_base_type *dst, | |
186 | const struct ovsdb_base_type *src) | |
187 | { | |
188 | *dst = *src; | |
189 | ||
bfc96d9b BP |
190 | if (src->enum_) { |
191 | dst->enum_ = xmalloc(sizeof *dst->enum_); | |
192 | ovsdb_datum_clone(dst->enum_, src->enum_, | |
193 | ovsdb_base_type_get_enum_type(dst->type)); | |
194 | } | |
195 | ||
bd76d25d BP |
196 | switch (dst->type) { |
197 | case OVSDB_TYPE_VOID: | |
198 | case OVSDB_TYPE_INTEGER: | |
199 | case OVSDB_TYPE_REAL: | |
200 | case OVSDB_TYPE_BOOLEAN: | |
201 | break; | |
202 | ||
203 | case OVSDB_TYPE_STRING: | |
bd76d25d BP |
204 | break; |
205 | ||
206 | case OVSDB_TYPE_UUID: | |
0d0f05b9 BP |
207 | if (dst->u.uuid.refTableName) { |
208 | dst->u.uuid.refTableName = xstrdup(dst->u.uuid.refTableName); | |
209 | } | |
bd76d25d BP |
210 | break; |
211 | ||
212 | case OVSDB_N_TYPES: | |
213 | default: | |
428b2edd | 214 | OVS_NOT_REACHED(); |
bd76d25d BP |
215 | } |
216 | } | |
217 | ||
218 | void | |
219 | ovsdb_base_type_destroy(struct ovsdb_base_type *base) | |
220 | { | |
221 | if (base) { | |
bfc96d9b BP |
222 | if (base->enum_) { |
223 | ovsdb_datum_destroy(base->enum_, | |
224 | ovsdb_base_type_get_enum_type(base->type)); | |
225 | free(base->enum_); | |
226 | } | |
227 | ||
bd76d25d BP |
228 | switch (base->type) { |
229 | case OVSDB_TYPE_VOID: | |
230 | case OVSDB_TYPE_INTEGER: | |
231 | case OVSDB_TYPE_REAL: | |
232 | case OVSDB_TYPE_BOOLEAN: | |
233 | break; | |
234 | ||
235 | case OVSDB_TYPE_STRING: | |
bd76d25d BP |
236 | break; |
237 | ||
238 | case OVSDB_TYPE_UUID: | |
0d0f05b9 | 239 | free(base->u.uuid.refTableName); |
bd76d25d BP |
240 | break; |
241 | ||
242 | case OVSDB_N_TYPES: | |
428b2edd | 243 | OVS_NOT_REACHED(); |
bd76d25d BP |
244 | |
245 | default: | |
428b2edd | 246 | OVS_NOT_REACHED(); |
bd76d25d BP |
247 | } |
248 | } | |
249 | } | |
250 | ||
251 | bool | |
252 | ovsdb_base_type_is_valid(const struct ovsdb_base_type *base) | |
253 | { | |
254 | switch (base->type) { | |
255 | case OVSDB_TYPE_VOID: | |
256 | return true; | |
257 | ||
258 | case OVSDB_TYPE_INTEGER: | |
259 | return base->u.integer.min <= base->u.integer.max; | |
260 | ||
261 | case OVSDB_TYPE_REAL: | |
262 | return base->u.real.min <= base->u.real.max; | |
263 | ||
264 | case OVSDB_TYPE_BOOLEAN: | |
265 | return true; | |
266 | ||
267 | case OVSDB_TYPE_STRING: | |
268 | return base->u.string.minLen <= base->u.string.maxLen; | |
269 | ||
270 | case OVSDB_TYPE_UUID: | |
271 | return true; | |
272 | ||
273 | case OVSDB_N_TYPES: | |
274 | default: | |
275 | return false; | |
276 | } | |
277 | } | |
278 | ||
279 | bool | |
280 | ovsdb_base_type_has_constraints(const struct ovsdb_base_type *base) | |
281 | { | |
bfc96d9b BP |
282 | if (base->enum_) { |
283 | return true; | |
284 | } | |
285 | ||
bd76d25d BP |
286 | switch (base->type) { |
287 | case OVSDB_TYPE_VOID: | |
428b2edd | 288 | OVS_NOT_REACHED(); |
bd76d25d BP |
289 | |
290 | case OVSDB_TYPE_INTEGER: | |
291 | return (base->u.integer.min != INT64_MIN | |
292 | || base->u.integer.max != INT64_MAX); | |
293 | ||
294 | case OVSDB_TYPE_REAL: | |
295 | return (base->u.real.min != -DBL_MAX | |
296 | || base->u.real.max != DBL_MAX); | |
297 | ||
298 | case OVSDB_TYPE_BOOLEAN: | |
299 | return false; | |
300 | ||
301 | case OVSDB_TYPE_STRING: | |
89521e3f | 302 | return base->u.string.minLen != 0 || base->u.string.maxLen != UINT_MAX; |
bd76d25d BP |
303 | |
304 | case OVSDB_TYPE_UUID: | |
0d0f05b9 | 305 | return base->u.uuid.refTableName != NULL; |
bd76d25d BP |
306 | |
307 | case OVSDB_N_TYPES: | |
428b2edd | 308 | OVS_NOT_REACHED(); |
bd76d25d BP |
309 | |
310 | default: | |
428b2edd | 311 | OVS_NOT_REACHED(); |
bd76d25d BP |
312 | } |
313 | } | |
314 | ||
315 | void | |
316 | ovsdb_base_type_clear_constraints(struct ovsdb_base_type *base) | |
317 | { | |
318 | enum ovsdb_atomic_type type = base->type; | |
319 | ovsdb_base_type_destroy(base); | |
320 | ovsdb_base_type_init(base, type); | |
321 | } | |
322 | ||
bd76d25d BP |
323 | static struct ovsdb_error * |
324 | parse_optional_uint(struct ovsdb_parser *parser, const char *member, | |
325 | unsigned int *uint) | |
326 | { | |
327 | const struct json *json; | |
328 | ||
329 | json = ovsdb_parser_member(parser, member, OP_INTEGER | OP_OPTIONAL); | |
330 | if (json) { | |
331 | if (json->u.integer < 0 || json->u.integer > UINT_MAX) { | |
332 | return ovsdb_syntax_error(json, NULL, | |
333 | "%s out of valid range 0 to %u", | |
334 | member, UINT_MAX); | |
335 | } | |
336 | *uint = json->u.integer; | |
337 | } | |
338 | return NULL; | |
339 | } | |
340 | ||
341 | struct ovsdb_error * | |
342 | ovsdb_base_type_from_json(struct ovsdb_base_type *base, | |
343 | const struct json *json) | |
344 | { | |
345 | struct ovsdb_parser parser; | |
346 | struct ovsdb_error *error; | |
bfc96d9b | 347 | const struct json *type, *enum_; |
bd76d25d BP |
348 | |
349 | if (json->type == JSON_STRING) { | |
350 | error = ovsdb_atomic_type_from_json(&base->type, json); | |
351 | if (error) { | |
352 | return error; | |
353 | } | |
354 | ovsdb_base_type_init(base, base->type); | |
355 | return NULL; | |
356 | } | |
357 | ||
358 | ovsdb_parser_init(&parser, json, "ovsdb type"); | |
359 | type = ovsdb_parser_member(&parser, "type", OP_STRING); | |
360 | if (ovsdb_parser_has_error(&parser)) { | |
361 | base->type = OVSDB_TYPE_VOID; | |
362 | return ovsdb_parser_finish(&parser); | |
363 | } | |
364 | ||
365 | error = ovsdb_atomic_type_from_json(&base->type, type); | |
366 | if (error) { | |
367 | return error; | |
368 | } | |
369 | ||
370 | ovsdb_base_type_init(base, base->type); | |
bfc96d9b BP |
371 | |
372 | enum_ = ovsdb_parser_member(&parser, "enum", OP_ANY | OP_OPTIONAL); | |
373 | if (enum_) { | |
374 | base->enum_ = xmalloc(sizeof *base->enum_); | |
375 | error = ovsdb_datum_from_json( | |
376 | base->enum_, ovsdb_base_type_get_enum_type(base->type), | |
377 | enum_, NULL); | |
378 | if (error) { | |
379 | free(base->enum_); | |
380 | base->enum_ = NULL; | |
381 | } | |
382 | } else if (base->type == OVSDB_TYPE_INTEGER) { | |
bd76d25d BP |
383 | const struct json *min, *max; |
384 | ||
385 | min = ovsdb_parser_member(&parser, "minInteger", | |
386 | OP_INTEGER | OP_OPTIONAL); | |
387 | max = ovsdb_parser_member(&parser, "maxInteger", | |
388 | OP_INTEGER | OP_OPTIONAL); | |
389 | base->u.integer.min = min ? min->u.integer : INT64_MIN; | |
390 | base->u.integer.max = max ? max->u.integer : INT64_MAX; | |
391 | if (base->u.integer.min > base->u.integer.max) { | |
392 | error = ovsdb_syntax_error(json, NULL, | |
393 | "minInteger exceeds maxInteger"); | |
394 | } | |
395 | } else if (base->type == OVSDB_TYPE_REAL) { | |
396 | const struct json *min, *max; | |
397 | ||
398 | min = ovsdb_parser_member(&parser, "minReal", OP_NUMBER | OP_OPTIONAL); | |
399 | max = ovsdb_parser_member(&parser, "maxReal", OP_NUMBER | OP_OPTIONAL); | |
400 | base->u.real.min = min ? json_real(min) : -DBL_MAX; | |
401 | base->u.real.max = max ? json_real(max) : DBL_MAX; | |
402 | if (base->u.real.min > base->u.real.max) { | |
403 | error = ovsdb_syntax_error(json, NULL, "minReal exceeds maxReal"); | |
404 | } | |
405 | } else if (base->type == OVSDB_TYPE_STRING) { | |
bd76d25d BP |
406 | if (!error) { |
407 | error = parse_optional_uint(&parser, "minLength", | |
408 | &base->u.string.minLen); | |
409 | } | |
410 | if (!error) { | |
411 | error = parse_optional_uint(&parser, "maxLength", | |
412 | &base->u.string.maxLen); | |
413 | } | |
414 | if (!error && base->u.string.minLen > base->u.string.maxLen) { | |
415 | error = ovsdb_syntax_error(json, NULL, | |
416 | "minLength exceeds maxLength"); | |
417 | } | |
0d0f05b9 BP |
418 | } else if (base->type == OVSDB_TYPE_UUID) { |
419 | const struct json *refTable; | |
420 | ||
421 | refTable = ovsdb_parser_member(&parser, "refTable", | |
422 | OP_ID | OP_OPTIONAL); | |
423 | if (refTable) { | |
7360012b BP |
424 | const struct json *refType; |
425 | ||
0d0f05b9 | 426 | base->u.uuid.refTableName = xstrdup(refTable->u.string); |
7360012b | 427 | |
0d0f05b9 BP |
428 | /* We can't set base->u.uuid.refTable here because we don't have |
429 | * enough context (we might not even be running in ovsdb-server). | |
430 | * ovsdb_create() will set refTable later. */ | |
7360012b BP |
431 | |
432 | refType = ovsdb_parser_member(&parser, "refType", | |
433 | OP_ID | OP_OPTIONAL); | |
434 | if (refType) { | |
435 | const char *refType_s = json_string(refType); | |
436 | if (!strcmp(refType_s, "strong")) { | |
437 | base->u.uuid.refType = OVSDB_REF_STRONG; | |
438 | } else if (!strcmp(refType_s, "weak")) { | |
439 | base->u.uuid.refType = OVSDB_REF_WEAK; | |
440 | } else { | |
441 | error = ovsdb_syntax_error(json, NULL, "refType must be " | |
442 | "\"strong\" or \"weak\" (not " | |
443 | "\"%s\")", refType_s); | |
444 | } | |
445 | } else { | |
446 | base->u.uuid.refType = OVSDB_REF_STRONG; | |
447 | } | |
0d0f05b9 | 448 | } |
bd76d25d BP |
449 | } |
450 | ||
451 | if (error) { | |
452 | ovsdb_error_destroy(ovsdb_parser_finish(&parser)); | |
453 | } else { | |
454 | error = ovsdb_parser_finish(&parser); | |
455 | } | |
456 | if (error) { | |
457 | ovsdb_base_type_destroy(base); | |
458 | base->type = OVSDB_TYPE_VOID; | |
459 | } | |
460 | return error; | |
461 | } | |
462 | ||
463 | struct json * | |
464 | ovsdb_base_type_to_json(const struct ovsdb_base_type *base) | |
465 | { | |
466 | struct json *json; | |
467 | ||
468 | if (!ovsdb_base_type_has_constraints(base)) { | |
469 | return json_string_create(ovsdb_atomic_type_to_string(base->type)); | |
470 | } | |
471 | ||
472 | json = json_object_create(); | |
473 | json_object_put_string(json, "type", | |
474 | ovsdb_atomic_type_to_string(base->type)); | |
bfc96d9b BP |
475 | |
476 | if (base->enum_) { | |
477 | const struct ovsdb_type *type; | |
478 | ||
479 | type = ovsdb_base_type_get_enum_type(base->type); | |
480 | json_object_put(json, "enum", ovsdb_datum_to_json(base->enum_, type)); | |
481 | } | |
482 | ||
bd76d25d BP |
483 | switch (base->type) { |
484 | case OVSDB_TYPE_VOID: | |
428b2edd | 485 | OVS_NOT_REACHED(); |
bd76d25d BP |
486 | |
487 | case OVSDB_TYPE_INTEGER: | |
488 | if (base->u.integer.min != INT64_MIN) { | |
489 | json_object_put(json, "minInteger", | |
490 | json_integer_create(base->u.integer.min)); | |
491 | } | |
492 | if (base->u.integer.max != INT64_MAX) { | |
493 | json_object_put(json, "maxInteger", | |
494 | json_integer_create(base->u.integer.max)); | |
495 | } | |
496 | break; | |
497 | ||
498 | case OVSDB_TYPE_REAL: | |
499 | if (base->u.real.min != -DBL_MAX) { | |
500 | json_object_put(json, "minReal", | |
501 | json_real_create(base->u.real.min)); | |
502 | } | |
503 | if (base->u.real.max != DBL_MAX) { | |
504 | json_object_put(json, "maxReal", | |
505 | json_real_create(base->u.real.max)); | |
506 | } | |
507 | break; | |
508 | ||
509 | case OVSDB_TYPE_BOOLEAN: | |
510 | break; | |
511 | ||
512 | case OVSDB_TYPE_STRING: | |
bd76d25d BP |
513 | if (base->u.string.minLen != 0) { |
514 | json_object_put(json, "minLength", | |
515 | json_integer_create(base->u.string.minLen)); | |
516 | } | |
517 | if (base->u.string.maxLen != UINT_MAX) { | |
518 | json_object_put(json, "maxLength", | |
519 | json_integer_create(base->u.string.maxLen)); | |
520 | } | |
521 | break; | |
522 | ||
523 | case OVSDB_TYPE_UUID: | |
0d0f05b9 BP |
524 | if (base->u.uuid.refTableName) { |
525 | json_object_put_string(json, "refTable", | |
526 | base->u.uuid.refTableName); | |
7360012b BP |
527 | if (base->u.uuid.refType == OVSDB_REF_WEAK) { |
528 | json_object_put_string(json, "refType", "weak"); | |
529 | } | |
0d0f05b9 | 530 | } |
bd76d25d BP |
531 | break; |
532 | ||
533 | case OVSDB_N_TYPES: | |
428b2edd | 534 | OVS_NOT_REACHED(); |
bd76d25d BP |
535 | |
536 | default: | |
428b2edd | 537 | OVS_NOT_REACHED(); |
bd76d25d BP |
538 | } |
539 | ||
540 | return json; | |
541 | } | |
542 | \f | |
543 | /* ovsdb_type */ | |
544 | ||
545 | void | |
546 | ovsdb_type_clone(struct ovsdb_type *dst, const struct ovsdb_type *src) | |
547 | { | |
548 | ovsdb_base_type_clone(&dst->key, &src->key); | |
549 | ovsdb_base_type_clone(&dst->value, &src->value); | |
550 | dst->n_min = src->n_min; | |
551 | dst->n_max = src->n_max; | |
552 | } | |
553 | ||
554 | void | |
555 | ovsdb_type_destroy(struct ovsdb_type *type) | |
556 | { | |
557 | ovsdb_base_type_destroy(&type->key); | |
558 | ovsdb_base_type_destroy(&type->value); | |
559 | } | |
560 | ||
561 | bool | |
562 | ovsdb_type_is_valid(const struct ovsdb_type *type) | |
563 | { | |
564 | return (type->key.type != OVSDB_TYPE_VOID | |
565 | && ovsdb_base_type_is_valid(&type->key) | |
566 | && ovsdb_base_type_is_valid(&type->value) | |
567 | && type->n_min <= 1 | |
ae8f13e2 | 568 | && type->n_max >= 1); |
bd76d25d | 569 | } |
f85f8ebb BP |
570 | |
571 | static struct ovsdb_error * | |
572 | n_from_json(const struct json *json, unsigned int *n) | |
573 | { | |
574 | if (!json) { | |
575 | return NULL; | |
576 | } else if (json->type == JSON_INTEGER | |
577 | && json->u.integer >= 0 && json->u.integer < UINT_MAX) { | |
578 | *n = json->u.integer; | |
579 | return NULL; | |
580 | } else { | |
581 | return ovsdb_syntax_error(json, NULL, "bad min or max value"); | |
582 | } | |
583 | } | |
584 | ||
585 | char * | |
586 | ovsdb_type_to_english(const struct ovsdb_type *type) | |
587 | { | |
bd76d25d BP |
588 | const char *key = ovsdb_atomic_type_to_string(type->key.type); |
589 | const char *value = ovsdb_atomic_type_to_string(type->value.type); | |
f85f8ebb BP |
590 | if (ovsdb_type_is_scalar(type)) { |
591 | return xstrdup(key); | |
592 | } else { | |
593 | struct ds s = DS_EMPTY_INITIALIZER; | |
594 | ds_put_cstr(&s, ovsdb_type_is_set(type) ? "set" : "map"); | |
595 | if (type->n_max == UINT_MAX) { | |
596 | if (type->n_min) { | |
597 | ds_put_format(&s, " of %u or more", type->n_min); | |
598 | } else { | |
599 | ds_put_cstr(&s, " of"); | |
600 | } | |
601 | } else if (type->n_min) { | |
602 | ds_put_format(&s, " of %u to %u", type->n_min, type->n_max); | |
603 | } else { | |
604 | ds_put_format(&s, " of up to %u", type->n_max); | |
605 | } | |
606 | if (ovsdb_type_is_set(type)) { | |
607 | ds_put_format(&s, " %ss", key); | |
608 | } else { | |
609 | ds_put_format(&s, " (%s, %s) pairs", key, value); | |
610 | } | |
611 | return ds_cstr(&s); | |
612 | } | |
613 | } | |
614 | ||
615 | struct ovsdb_error * | |
616 | ovsdb_type_from_json(struct ovsdb_type *type, const struct json *json) | |
617 | { | |
bfc96d9b | 618 | ovsdb_base_type_init(&type->value, OVSDB_TYPE_VOID); |
f85f8ebb BP |
619 | type->n_min = 1; |
620 | type->n_max = 1; | |
621 | ||
622 | if (json->type == JSON_STRING) { | |
bd76d25d | 623 | return ovsdb_base_type_from_json(&type->key, json); |
f85f8ebb BP |
624 | } else if (json->type == JSON_OBJECT) { |
625 | const struct json *key, *value, *min, *max; | |
626 | struct ovsdb_error *error; | |
627 | struct ovsdb_parser parser; | |
628 | ||
629 | ovsdb_parser_init(&parser, json, "ovsdb type"); | |
bd76d25d BP |
630 | key = ovsdb_parser_member(&parser, "key", OP_STRING | OP_OBJECT); |
631 | value = ovsdb_parser_member(&parser, "value", | |
632 | OP_STRING | OP_OBJECT | OP_OPTIONAL); | |
f85f8ebb BP |
633 | min = ovsdb_parser_member(&parser, "min", OP_INTEGER | OP_OPTIONAL); |
634 | max = ovsdb_parser_member(&parser, "max", | |
635 | OP_INTEGER | OP_STRING | OP_OPTIONAL); | |
636 | error = ovsdb_parser_finish(&parser); | |
637 | if (error) { | |
638 | return error; | |
639 | } | |
640 | ||
bd76d25d | 641 | error = ovsdb_base_type_from_json(&type->key, key); |
f85f8ebb BP |
642 | if (error) { |
643 | return error; | |
644 | } | |
645 | ||
646 | if (value) { | |
bd76d25d | 647 | error = ovsdb_base_type_from_json(&type->value, value); |
f85f8ebb BP |
648 | if (error) { |
649 | return error; | |
650 | } | |
651 | } | |
652 | ||
653 | error = n_from_json(min, &type->n_min); | |
654 | if (error) { | |
655 | return error; | |
656 | } | |
657 | ||
658 | if (max && max->type == JSON_STRING | |
659 | && !strcmp(max->u.string, "unlimited")) { | |
660 | type->n_max = UINT_MAX; | |
661 | } else { | |
662 | error = n_from_json(max, &type->n_max); | |
663 | if (error) { | |
664 | return error; | |
665 | } | |
666 | } | |
667 | ||
668 | if (!ovsdb_type_is_valid(type)) { | |
669 | return ovsdb_syntax_error(json, NULL, | |
670 | "ovsdb type fails constraint checks"); | |
671 | } | |
672 | ||
673 | return NULL; | |
674 | } else { | |
675 | return ovsdb_syntax_error(json, NULL, "ovsdb type expected"); | |
676 | } | |
677 | } | |
678 | ||
679 | struct json * | |
680 | ovsdb_type_to_json(const struct ovsdb_type *type) | |
681 | { | |
bd76d25d BP |
682 | if (ovsdb_type_is_scalar(type) |
683 | && !ovsdb_base_type_has_constraints(&type->key)) { | |
684 | return ovsdb_base_type_to_json(&type->key); | |
f85f8ebb BP |
685 | } else { |
686 | struct json *json = json_object_create(); | |
bd76d25d BP |
687 | json_object_put(json, "key", ovsdb_base_type_to_json(&type->key)); |
688 | if (type->value.type != OVSDB_TYPE_VOID) { | |
f85f8ebb | 689 | json_object_put(json, "value", |
bd76d25d | 690 | ovsdb_base_type_to_json(&type->value)); |
f85f8ebb BP |
691 | } |
692 | if (type->n_min != 1) { | |
693 | json_object_put(json, "min", json_integer_create(type->n_min)); | |
694 | } | |
695 | if (type->n_max == UINT_MAX) { | |
696 | json_object_put_string(json, "max", "unlimited"); | |
697 | } else if (type->n_max != 1) { | |
698 | json_object_put(json, "max", json_integer_create(type->n_max)); | |
699 | } | |
700 | return json; | |
701 | } | |
702 | } |