/src/openvswitch/lib/ovsdb-data.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (c) 2009, 2010, 2011, 2012, 2014, 2016, 2017 Nicira, Inc. |
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 <ctype.h> |
21 | | #include <float.h> |
22 | | #include <inttypes.h> |
23 | | #include <limits.h> |
24 | | |
25 | | #include "openvswitch/dynamic-string.h" |
26 | | #include "hash.h" |
27 | | #include "ovs-thread.h" |
28 | | #include "ovsdb-error.h" |
29 | | #include "ovsdb-parser.h" |
30 | | #include "openvswitch/json.h" |
31 | | #include "openvswitch/shash.h" |
32 | | #include "smap.h" |
33 | | #include "sort.h" |
34 | | #include "unicode.h" |
35 | | #include "util.h" |
36 | | |
37 | | static struct json * |
38 | | wrap_json(const char *name, struct json *wrapped) |
39 | 0 | { |
40 | 0 | return json_array_create_2(json_string_create(name), wrapped); |
41 | 0 | } |
42 | | |
43 | | /* Initializes 'atom' with the default value of the given 'type'. |
44 | | * |
45 | | * The default value for an atom is as defined in RFC 7047: |
46 | | * |
47 | | * - "integer" or "real": 0 |
48 | | * |
49 | | * - "boolean": false |
50 | | * |
51 | | * - "string": "" (the empty string) |
52 | | * |
53 | | * - "uuid": 00000000-0000-0000-0000-000000000000 |
54 | | * |
55 | | * The caller must eventually arrange for 'atom' to be destroyed (with |
56 | | * ovsdb_atom_destroy()). */ |
57 | | void |
58 | | ovsdb_atom_init_default(union ovsdb_atom *atom, enum ovsdb_atomic_type type) |
59 | 0 | { |
60 | 0 | switch (type) { |
61 | 0 | case OVSDB_TYPE_VOID: |
62 | 0 | OVS_NOT_REACHED(); |
63 | | |
64 | 0 | case OVSDB_TYPE_INTEGER: |
65 | 0 | atom->integer = 0; |
66 | 0 | break; |
67 | | |
68 | 0 | case OVSDB_TYPE_REAL: |
69 | 0 | atom->real = 0.0; |
70 | 0 | break; |
71 | | |
72 | 0 | case OVSDB_TYPE_BOOLEAN: |
73 | 0 | atom->boolean = false; |
74 | 0 | break; |
75 | | |
76 | 0 | case OVSDB_TYPE_STRING: |
77 | 0 | atom->s = ovsdb_atom_string_create_nocopy(xmemdup("", 1)); |
78 | 0 | break; |
79 | | |
80 | 0 | case OVSDB_TYPE_UUID: |
81 | 0 | uuid_zero(&atom->uuid); |
82 | 0 | break; |
83 | | |
84 | 0 | case OVSDB_N_TYPES: |
85 | 0 | default: |
86 | 0 | OVS_NOT_REACHED(); |
87 | 0 | } |
88 | 0 | } |
89 | | |
90 | | /* Returns a read-only atom of the given 'type' that has the default value for |
91 | | * 'type'. The caller must not modify or free the returned atom. |
92 | | * |
93 | | * See ovsdb_atom_init_default() for an explanation of the default value of an |
94 | | * atom. */ |
95 | | const union ovsdb_atom * |
96 | | ovsdb_atom_default(enum ovsdb_atomic_type type) |
97 | 0 | { |
98 | 0 | static union ovsdb_atom default_atoms[OVSDB_N_TYPES]; |
99 | 0 | static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; |
100 | |
|
101 | 0 | if (ovsthread_once_start(&once)) { |
102 | 0 | int i; |
103 | |
|
104 | 0 | for (i = 0; i < OVSDB_N_TYPES; i++) { |
105 | 0 | if (i != OVSDB_TYPE_VOID) { |
106 | 0 | ovsdb_atom_init_default(&default_atoms[i], i); |
107 | 0 | } |
108 | 0 | } |
109 | 0 | ovsthread_once_done(&once); |
110 | 0 | } |
111 | |
|
112 | 0 | ovs_assert(ovsdb_atomic_type_is_valid(type)); |
113 | 0 | return &default_atoms[type]; |
114 | 0 | } |
115 | | |
116 | | /* Returns true if 'atom', which must have the given 'type', has the default |
117 | | * value for that type. |
118 | | * |
119 | | * See ovsdb_atom_init_default() for an explanation of the default value of an |
120 | | * atom. */ |
121 | | bool |
122 | | ovsdb_atom_is_default(const union ovsdb_atom *atom, |
123 | | enum ovsdb_atomic_type type) |
124 | 0 | { |
125 | 0 | switch (type) { |
126 | 0 | case OVSDB_TYPE_VOID: |
127 | 0 | OVS_NOT_REACHED(); |
128 | | |
129 | 0 | case OVSDB_TYPE_INTEGER: |
130 | 0 | return atom->integer == 0; |
131 | | |
132 | 0 | case OVSDB_TYPE_REAL: |
133 | 0 | return atom->real == 0.0; |
134 | | |
135 | 0 | case OVSDB_TYPE_BOOLEAN: |
136 | 0 | return atom->boolean == false; |
137 | | |
138 | 0 | case OVSDB_TYPE_STRING: |
139 | 0 | return json_string(atom->s)[0] == '\0'; |
140 | | |
141 | 0 | case OVSDB_TYPE_UUID: |
142 | 0 | return uuid_is_zero(&atom->uuid); |
143 | | |
144 | 0 | case OVSDB_N_TYPES: |
145 | 0 | default: |
146 | 0 | OVS_NOT_REACHED(); |
147 | 0 | } |
148 | 0 | } |
149 | | |
150 | | /* Initializes 'new' as a copy of 'old', with the given 'type'. |
151 | | * |
152 | | * The caller must eventually arrange for 'new' to be destroyed (with |
153 | | * ovsdb_atom_destroy()). */ |
154 | | void |
155 | | ovsdb_atom_clone(union ovsdb_atom *new, const union ovsdb_atom *old, |
156 | | enum ovsdb_atomic_type type) |
157 | 0 | { |
158 | 0 | switch (type) { |
159 | 0 | case OVSDB_TYPE_VOID: |
160 | 0 | OVS_NOT_REACHED(); |
161 | | |
162 | 0 | case OVSDB_TYPE_INTEGER: |
163 | 0 | new->integer = old->integer; |
164 | 0 | break; |
165 | | |
166 | 0 | case OVSDB_TYPE_REAL: |
167 | 0 | new->real = old->real; |
168 | 0 | break; |
169 | | |
170 | 0 | case OVSDB_TYPE_BOOLEAN: |
171 | 0 | new->boolean = old->boolean; |
172 | 0 | break; |
173 | | |
174 | 0 | case OVSDB_TYPE_STRING: |
175 | 0 | new->s = json_clone(old->s); |
176 | 0 | break; |
177 | | |
178 | 0 | case OVSDB_TYPE_UUID: |
179 | 0 | new->uuid = old->uuid; |
180 | 0 | break; |
181 | | |
182 | 0 | case OVSDB_N_TYPES: |
183 | 0 | default: |
184 | 0 | OVS_NOT_REACHED(); |
185 | 0 | } |
186 | 0 | } |
187 | | |
188 | | /* Swaps the contents of 'a' and 'b', which need not have the same type. */ |
189 | | void |
190 | | ovsdb_atom_swap(union ovsdb_atom *a, union ovsdb_atom *b) |
191 | 0 | { |
192 | 0 | union ovsdb_atom tmp = *a; |
193 | 0 | *a = *b; |
194 | 0 | *b = tmp; |
195 | 0 | } |
196 | | |
197 | | /* Returns a hash value for 'atom', which has the specified 'type', folding |
198 | | * 'basis' into the calculation. */ |
199 | | uint32_t |
200 | | ovsdb_atom_hash(const union ovsdb_atom *atom, enum ovsdb_atomic_type type, |
201 | | uint32_t basis) |
202 | 0 | { |
203 | 0 | switch (type) { |
204 | 0 | case OVSDB_TYPE_VOID: |
205 | 0 | OVS_NOT_REACHED(); |
206 | | |
207 | 0 | case OVSDB_TYPE_INTEGER: |
208 | 0 | return hash_int(atom->integer, basis); |
209 | | |
210 | 0 | case OVSDB_TYPE_REAL: |
211 | 0 | return hash_double(atom->real, basis); |
212 | | |
213 | 0 | case OVSDB_TYPE_BOOLEAN: |
214 | 0 | return hash_boolean(atom->boolean, basis); |
215 | | |
216 | 0 | case OVSDB_TYPE_STRING: |
217 | 0 | return json_hash(atom->s, basis); |
218 | | |
219 | 0 | case OVSDB_TYPE_UUID: |
220 | 0 | return hash_int(uuid_hash(&atom->uuid), basis); |
221 | | |
222 | 0 | case OVSDB_N_TYPES: |
223 | 0 | default: |
224 | 0 | OVS_NOT_REACHED(); |
225 | 0 | } |
226 | 0 | } |
227 | | |
228 | | /* Compares 'a' and 'b', which both have type 'type', and returns a |
229 | | * strcmp()-like result. */ |
230 | | int |
231 | | ovsdb_atom_compare_3way(const union ovsdb_atom *a, |
232 | | const union ovsdb_atom *b, |
233 | | enum ovsdb_atomic_type type) |
234 | 0 | { |
235 | 0 | switch (type) { |
236 | 0 | case OVSDB_TYPE_VOID: |
237 | 0 | OVS_NOT_REACHED(); |
238 | | |
239 | 0 | case OVSDB_TYPE_INTEGER: |
240 | 0 | return a->integer < b->integer ? -1 : a->integer > b->integer; |
241 | | |
242 | 0 | case OVSDB_TYPE_REAL: |
243 | 0 | return a->real < b->real ? -1 : a->real > b->real; |
244 | | |
245 | 0 | case OVSDB_TYPE_BOOLEAN: |
246 | 0 | return a->boolean - b->boolean; |
247 | | |
248 | 0 | case OVSDB_TYPE_STRING: |
249 | 0 | return a->s == b->s ? 0 : strcmp(json_string(a->s), json_string(b->s)); |
250 | | |
251 | 0 | case OVSDB_TYPE_UUID: |
252 | 0 | return uuid_compare_3way(&a->uuid, &b->uuid); |
253 | | |
254 | 0 | case OVSDB_N_TYPES: |
255 | 0 | default: |
256 | 0 | OVS_NOT_REACHED(); |
257 | 0 | } |
258 | 0 | } |
259 | | |
260 | | static struct ovsdb_error * |
261 | | unwrap_json(const struct json *json, const char *name, |
262 | | enum json_type value_type, const struct json **value) |
263 | 0 | { |
264 | 0 | if (json->type != JSON_ARRAY |
265 | 0 | || json->array.n != 2 |
266 | 0 | || json->array.elems[0]->type != JSON_STRING |
267 | 0 | || (name && strcmp(json->array.elems[0]->string, name)) |
268 | 0 | || json->array.elems[1]->type != value_type) |
269 | 0 | { |
270 | 0 | *value = NULL; |
271 | 0 | return ovsdb_syntax_error(json, NULL, "expected [\"%s\", <%s>]", name, |
272 | 0 | json_type_to_string(value_type)); |
273 | 0 | } |
274 | 0 | *value = json->array.elems[1]; |
275 | 0 | return NULL; |
276 | 0 | } |
277 | | |
278 | | static struct ovsdb_error * |
279 | | parse_json_pair(const struct json *json, |
280 | | const struct json **elem0, const struct json **elem1) |
281 | 0 | { |
282 | 0 | if (json->type != JSON_ARRAY || json->array.n != 2) { |
283 | 0 | return ovsdb_syntax_error(json, NULL, "expected 2-element array"); |
284 | 0 | } |
285 | 0 | *elem0 = json->array.elems[0]; |
286 | 0 | *elem1 = json->array.elems[1]; |
287 | 0 | return NULL; |
288 | 0 | } |
289 | | |
290 | | static void |
291 | | ovsdb_symbol_referenced(struct ovsdb_symbol *symbol, |
292 | | const struct ovsdb_base_type *base) |
293 | 0 | { |
294 | 0 | ovs_assert(base->type == OVSDB_TYPE_UUID); |
295 | |
|
296 | 0 | if (base->uuid.refTableName) { |
297 | 0 | switch (base->uuid.refType) { |
298 | 0 | case OVSDB_REF_STRONG: |
299 | 0 | symbol->strong_ref = true; |
300 | 0 | break; |
301 | 0 | case OVSDB_REF_WEAK: |
302 | 0 | symbol->weak_ref = true; |
303 | 0 | break; |
304 | 0 | } |
305 | 0 | } |
306 | 0 | } |
307 | | |
308 | | static union ovsdb_atom * |
309 | | alloc_default_atoms(enum ovsdb_atomic_type type, size_t n) |
310 | 0 | { |
311 | 0 | if (type != OVSDB_TYPE_VOID && n) { |
312 | 0 | union ovsdb_atom *atoms; |
313 | 0 | unsigned int i; |
314 | |
|
315 | 0 | atoms = xmalloc(n * sizeof *atoms); |
316 | 0 | for (i = 0; i < n; i++) { |
317 | 0 | ovsdb_atom_init_default(&atoms[i], type); |
318 | 0 | } |
319 | 0 | return atoms; |
320 | 0 | } else { |
321 | | /* Avoid wasting memory in the n == 0 case, because xmalloc(0) is |
322 | | * treated as xmalloc(1). */ |
323 | 0 | return NULL; |
324 | 0 | } |
325 | 0 | } |
326 | | |
327 | | static struct ovsdb_error * OVS_WARN_UNUSED_RESULT |
328 | | ovsdb_atom_parse_uuid(struct uuid *uuid, const struct json *json, |
329 | | struct ovsdb_symbol_table *symtab, |
330 | | const struct ovsdb_base_type *base) |
331 | 0 | { |
332 | 0 | struct ovsdb_error *error0; |
333 | 0 | const struct json *value; |
334 | |
|
335 | 0 | error0 = unwrap_json(json, "uuid", JSON_STRING, &value); |
336 | 0 | if (!error0) { |
337 | 0 | const char *uuid_string = json_string(value); |
338 | 0 | if (!uuid_from_string(uuid, uuid_string)) { |
339 | 0 | return ovsdb_syntax_error(json, NULL, "\"%s\" is not a valid UUID", |
340 | 0 | uuid_string); |
341 | 0 | } |
342 | 0 | } else if (symtab) { |
343 | 0 | struct ovsdb_error *error1; |
344 | |
|
345 | 0 | error1 = unwrap_json(json, "named-uuid", JSON_STRING, &value); |
346 | 0 | if (!error1) { |
347 | 0 | struct ovsdb_symbol *symbol; |
348 | |
|
349 | 0 | ovsdb_error_destroy(error0); |
350 | 0 | if (!ovsdb_parser_is_id(json_string(value))) { |
351 | 0 | return ovsdb_syntax_error(json, NULL, "named-uuid string is " |
352 | 0 | "not a valid <id>"); |
353 | 0 | } |
354 | | |
355 | 0 | symbol = ovsdb_symbol_table_insert(symtab, json_string(value)); |
356 | 0 | *uuid = symbol->uuid; |
357 | 0 | ovsdb_symbol_referenced(symbol, base); |
358 | 0 | return NULL; |
359 | 0 | } |
360 | 0 | ovsdb_error_destroy(error1); |
361 | 0 | } |
362 | | |
363 | 0 | return error0; |
364 | 0 | } |
365 | | |
366 | | static struct ovsdb_error * OVS_WARN_UNUSED_RESULT |
367 | | ovsdb_atom_from_json__(union ovsdb_atom *atom, |
368 | | const struct ovsdb_base_type *base, |
369 | | const struct json *json, |
370 | | struct ovsdb_symbol_table *symtab) |
371 | 0 | { |
372 | 0 | enum ovsdb_atomic_type type = base->type; |
373 | |
|
374 | 0 | switch (type) { |
375 | 0 | case OVSDB_TYPE_VOID: |
376 | 0 | OVS_NOT_REACHED(); |
377 | | |
378 | 0 | case OVSDB_TYPE_INTEGER: |
379 | 0 | if (json->type == JSON_INTEGER) { |
380 | 0 | atom->integer = json->integer; |
381 | 0 | return NULL; |
382 | 0 | } |
383 | 0 | break; |
384 | | |
385 | 0 | case OVSDB_TYPE_REAL: |
386 | 0 | if (json->type == JSON_INTEGER) { |
387 | 0 | atom->real = json->integer; |
388 | 0 | return NULL; |
389 | 0 | } else if (json->type == JSON_REAL) { |
390 | 0 | atom->real = json->real; |
391 | 0 | return NULL; |
392 | 0 | } |
393 | 0 | break; |
394 | | |
395 | 0 | case OVSDB_TYPE_BOOLEAN: |
396 | 0 | if (json->type == JSON_TRUE) { |
397 | 0 | atom->boolean = true; |
398 | 0 | return NULL; |
399 | 0 | } else if (json->type == JSON_FALSE) { |
400 | 0 | atom->boolean = false; |
401 | 0 | return NULL; |
402 | 0 | } |
403 | 0 | break; |
404 | | |
405 | 0 | case OVSDB_TYPE_STRING: |
406 | 0 | if (json->type == JSON_STRING) { |
407 | 0 | atom->s = json_clone(json); |
408 | 0 | return NULL; |
409 | 0 | } |
410 | 0 | break; |
411 | | |
412 | 0 | case OVSDB_TYPE_UUID: |
413 | 0 | return ovsdb_atom_parse_uuid(&atom->uuid, json, symtab, base); |
414 | | |
415 | 0 | case OVSDB_N_TYPES: |
416 | 0 | default: |
417 | 0 | OVS_NOT_REACHED(); |
418 | 0 | } |
419 | | |
420 | 0 | return ovsdb_syntax_error(json, NULL, "expected %s", |
421 | 0 | ovsdb_atomic_type_to_string(type)); |
422 | 0 | } |
423 | | |
424 | | /* Parses 'json' as an atom of the type described by 'base'. If successful, |
425 | | * returns NULL and initializes 'atom' with the parsed atom. On failure, |
426 | | * returns an error and the contents of 'atom' are indeterminate. The caller |
427 | | * is responsible for freeing the error or the atom that is returned. |
428 | | * |
429 | | * Violations of constraints expressed by 'base' are treated as errors. |
430 | | * |
431 | | * If 'symtab' is nonnull, then named UUIDs in 'symtab' are accepted. Refer to |
432 | | * RFC 7047 for information about this, and for the syntax that this function |
433 | | * accepts. If 'base' is a reference and a symbol is parsed, then the symbol's |
434 | | * 'strong_ref' or 'weak_ref' member is set to true, as appropriate. */ |
435 | | struct ovsdb_error * |
436 | | ovsdb_atom_from_json(union ovsdb_atom *atom, |
437 | | const struct ovsdb_base_type *base, |
438 | | const struct json *json, |
439 | | struct ovsdb_symbol_table *symtab) |
440 | 0 | { |
441 | 0 | struct ovsdb_error *error; |
442 | |
|
443 | 0 | error = ovsdb_atom_from_json__(atom, base, json, symtab); |
444 | 0 | if (error) { |
445 | 0 | return error; |
446 | 0 | } |
447 | | |
448 | 0 | error = ovsdb_atom_check_constraints(atom, base); |
449 | 0 | if (error) { |
450 | 0 | ovsdb_atom_destroy(atom, base->type); |
451 | 0 | } |
452 | 0 | return error; |
453 | 0 | } |
454 | | |
455 | | /* Converts 'atom', of the specified 'type', to JSON format, and returns the |
456 | | * JSON. The caller is responsible for freeing the returned JSON. |
457 | | * |
458 | | * If 'allow_shallow_copies' is false, deep copy of the string JSON object |
459 | | * will be used. Useful when the same string object is accessed by multiple |
460 | | * threads as deep copy will not change the reference counter of the original |
461 | | * JSON string. |
462 | | * |
463 | | * Refer to RFC 7047 for the format of the JSON that this function produces. */ |
464 | | static struct json * |
465 | | ovsdb_atom_to_json__(const union ovsdb_atom *atom, enum ovsdb_atomic_type type, |
466 | | bool allow_shallow_copies) |
467 | 0 | { |
468 | 0 | switch (type) { |
469 | 0 | case OVSDB_TYPE_VOID: |
470 | 0 | OVS_NOT_REACHED(); |
471 | | |
472 | 0 | case OVSDB_TYPE_INTEGER: |
473 | 0 | return json_integer_create(atom->integer); |
474 | | |
475 | 0 | case OVSDB_TYPE_REAL: |
476 | 0 | return json_real_create(atom->real); |
477 | | |
478 | 0 | case OVSDB_TYPE_BOOLEAN: |
479 | 0 | return json_boolean_create(atom->boolean); |
480 | | |
481 | 0 | case OVSDB_TYPE_STRING: |
482 | 0 | return allow_shallow_copies ? json_clone(atom->s) |
483 | 0 | : json_deep_clone(atom->s); |
484 | | |
485 | 0 | case OVSDB_TYPE_UUID: |
486 | 0 | return wrap_json("uuid", json_string_create_nocopy( |
487 | 0 | xasprintf(UUID_FMT, UUID_ARGS(&atom->uuid)))); |
488 | | |
489 | 0 | case OVSDB_N_TYPES: |
490 | 0 | default: |
491 | 0 | OVS_NOT_REACHED(); |
492 | 0 | } |
493 | 0 | } |
494 | | |
495 | | struct json * |
496 | | ovsdb_atom_to_json(const union ovsdb_atom *atom, enum ovsdb_atomic_type type) |
497 | 0 | { |
498 | 0 | return ovsdb_atom_to_json__(atom, type, true); |
499 | 0 | } |
500 | | |
501 | | static struct json * |
502 | | ovsdb_atom_to_json_deep(const union ovsdb_atom *atom, |
503 | | enum ovsdb_atomic_type type) |
504 | 0 | { |
505 | 0 | return ovsdb_atom_to_json__(atom, type, false); |
506 | 0 | } |
507 | | |
508 | | static char * |
509 | | ovsdb_atom_from_string__(union ovsdb_atom *atom, |
510 | | union ovsdb_atom **range_end_atom, |
511 | | const struct ovsdb_base_type *base, const char *s, |
512 | | struct ovsdb_symbol_table *symtab) |
513 | 0 | { |
514 | 0 | enum ovsdb_atomic_type type = base->type; |
515 | |
|
516 | 0 | switch (type) { |
517 | 0 | case OVSDB_TYPE_VOID: |
518 | 0 | OVS_NOT_REACHED(); |
519 | | |
520 | 0 | case OVSDB_TYPE_INTEGER: { |
521 | 0 | long long int integer, end; |
522 | 0 | if (range_end_atom |
523 | 0 | && str_to_llong_range(s, 10, &integer, &end)) { |
524 | 0 | if (end < integer) { |
525 | 0 | return xasprintf("\"%s\" is not a valid range. " |
526 | 0 | "Range end cannot be before start.", s); |
527 | 0 | } |
528 | 0 | *range_end_atom = alloc_default_atoms(type, 1); |
529 | 0 | if (!(*range_end_atom)) { |
530 | 0 | return xasprintf("\"%s\" is not a valid range", s); |
531 | 0 | } |
532 | 0 | (*range_end_atom)->integer = end; |
533 | 0 | } else if (!str_to_llong(s, 10, &integer)) { |
534 | 0 | return xasprintf("\"%s\" is not a valid integer or range", s); |
535 | 0 | } |
536 | 0 | atom->integer = integer; |
537 | 0 | } |
538 | 0 | break; |
539 | | |
540 | 0 | case OVSDB_TYPE_REAL: |
541 | 0 | if (!str_to_double(s, &atom->real)) { |
542 | 0 | return xasprintf("\"%s\" is not a valid real number", s); |
543 | 0 | } |
544 | | /* Our JSON input routines map negative zero to zero, so do that here |
545 | | * too for consistency. */ |
546 | 0 | if (atom->real == 0.0) { |
547 | 0 | atom->real = 0.0; |
548 | 0 | } |
549 | 0 | break; |
550 | | |
551 | 0 | case OVSDB_TYPE_BOOLEAN: |
552 | 0 | if (!strcmp(s, "true") || !strcmp(s, "yes") || !strcmp(s, "on") |
553 | 0 | || !strcmp(s, "1")) { |
554 | 0 | atom->boolean = true; |
555 | 0 | } else if (!strcmp(s, "false") || !strcmp(s, "no") || !strcmp(s, "off") |
556 | 0 | || !strcmp(s, "0")) { |
557 | 0 | atom->boolean = false; |
558 | 0 | } else { |
559 | 0 | return xasprintf("\"%s\" is not a valid boolean " |
560 | 0 | "(use \"true\" or \"false\")", s); |
561 | 0 | } |
562 | 0 | break; |
563 | | |
564 | 0 | case OVSDB_TYPE_STRING: |
565 | 0 | if (*s == '\0') { |
566 | 0 | return xstrdup("An empty string is not valid as input; " |
567 | 0 | "use \"\" to represent the empty string"); |
568 | 0 | } else if (*s == '"') { |
569 | 0 | size_t s_len = strlen(s); |
570 | |
|
571 | 0 | if (s_len < 2 || s[s_len - 1] != '"') { |
572 | 0 | return xasprintf("%s: missing quote at end of " |
573 | 0 | "quoted string", s); |
574 | 0 | } else { |
575 | 0 | char *res; |
576 | 0 | if (json_string_unescape(s + 1, s_len - 2, &res)) { |
577 | 0 | atom->s = ovsdb_atom_string_create_nocopy(res); |
578 | 0 | } else { |
579 | 0 | char *error = xasprintf("%s: %s", s, res); |
580 | 0 | free(res); |
581 | 0 | return error; |
582 | 0 | } |
583 | 0 | } |
584 | 0 | } else { |
585 | 0 | atom->s = ovsdb_atom_string_create(s); |
586 | 0 | } |
587 | 0 | break; |
588 | | |
589 | 0 | case OVSDB_TYPE_UUID: |
590 | 0 | if (*s == '@') { |
591 | 0 | struct ovsdb_symbol *symbol = ovsdb_symbol_table_insert(symtab, s); |
592 | 0 | atom->uuid = symbol->uuid; |
593 | 0 | ovsdb_symbol_referenced(symbol, base); |
594 | 0 | } else if (!uuid_from_string(&atom->uuid, s)) { |
595 | 0 | return xasprintf("\"%s\" is not a valid UUID", s); |
596 | 0 | } |
597 | 0 | break; |
598 | | |
599 | 0 | case OVSDB_N_TYPES: |
600 | 0 | default: |
601 | 0 | OVS_NOT_REACHED(); |
602 | 0 | } |
603 | | |
604 | 0 | return NULL; |
605 | 0 | } |
606 | | |
607 | | /* Initializes 'atom' and optionally 'range_end_atom' to a value of type 'base' |
608 | | * parsed from 's', which takes one of the following forms: |
609 | | * |
610 | | * - OVSDB_TYPE_INTEGER: A decimal integer optionally preceded by a sign |
611 | | * or two decimal integers optionally preceded by a sign and separated |
612 | | * by a hyphen, representing inclusive range of integers |
613 | | * ['atom', 'range_end_atom']. |
614 | | * |
615 | | * - OVSDB_TYPE_REAL: A floating-point number in the format accepted by |
616 | | * strtod(). |
617 | | * |
618 | | * - OVSDB_TYPE_BOOLEAN: "true", "yes", "on", "1" for true, or "false", |
619 | | * "no", "off", or "0" for false. |
620 | | * |
621 | | * - OVSDB_TYPE_STRING: A JSON string if it begins with a quote, otherwise |
622 | | * an arbitrary string. |
623 | | * |
624 | | * - OVSDB_TYPE_UUID: A UUID in RFC 4122 format. If 'symtab' is nonnull, |
625 | | * then an identifier beginning with '@' is also acceptable. If the |
626 | | * named identifier is already in 'symtab', then the associated UUID is |
627 | | * used; otherwise, a new, random UUID is used and added to the symbol |
628 | | * table. If 'base' is a reference and a symbol is parsed, then the |
629 | | * symbol's 'strong_ref' or 'weak_ref' member is set to true, as |
630 | | * appropriate. |
631 | | * |
632 | | * Returns a null pointer if successful, otherwise an error message describing |
633 | | * the problem. On failure, the contents of 'atom' are indeterminate. The |
634 | | * caller is responsible for freeing the atom or the error. |
635 | | * |
636 | | * Does not attempt to parse range if 'range_end_atom' is a null pointer. |
637 | | * Dynamically allocates ovdsb_atom and stores its address in '*range_end_atom' |
638 | | * if successfully parses range. Caller is responsible for deallocating |
639 | | * the memory by calling 'ovsdb_atom_destroy' and then 'free' on the address. |
640 | | * Does not allocate memory and sets '*range_end_atom' to a null pointer |
641 | | * if does not parse a range or fails for any reason. |
642 | | */ |
643 | | char * |
644 | | ovsdb_atom_from_string(union ovsdb_atom *atom, |
645 | | union ovsdb_atom **range_end_atom, |
646 | | const struct ovsdb_base_type *base, const char *s, |
647 | | struct ovsdb_symbol_table *symtab) |
648 | 0 | { |
649 | 0 | struct ovsdb_error *error; |
650 | 0 | char *msg; |
651 | |
|
652 | 0 | if (range_end_atom) { |
653 | 0 | *range_end_atom = NULL; |
654 | 0 | } |
655 | |
|
656 | 0 | msg = ovsdb_atom_from_string__(atom, range_end_atom, base, s, symtab); |
657 | 0 | if (msg) { |
658 | 0 | return msg; |
659 | 0 | } |
660 | | |
661 | 0 | error = ovsdb_atom_check_constraints(atom, base); |
662 | |
|
663 | 0 | if (!error && range_end_atom && *range_end_atom) { |
664 | | /* Check range constraints */ |
665 | 0 | int64_t start = atom->integer; |
666 | 0 | int64_t end = (*range_end_atom)->integer; |
667 | 0 | if (base->enum_) { |
668 | 0 | for (int64_t i = start + 1; i <= end; i++) { |
669 | 0 | union ovsdb_atom ai = { .integer = i }; |
670 | 0 | error = ovsdb_atom_check_constraints(&ai, base); |
671 | 0 | if (error) { |
672 | 0 | break; |
673 | 0 | } |
674 | 0 | } |
675 | 0 | } else { |
676 | 0 | error = ovsdb_atom_check_constraints(*range_end_atom, base); |
677 | 0 | } |
678 | |
|
679 | 0 | if (!error) { |
680 | 0 | error = ovsdb_atom_range_check_size(start, end); |
681 | 0 | } |
682 | 0 | } |
683 | |
|
684 | 0 | if (error) { |
685 | 0 | ovsdb_atom_destroy(atom, base->type); |
686 | 0 | if (range_end_atom && *range_end_atom) { |
687 | 0 | ovsdb_atom_destroy(*range_end_atom, base->type); |
688 | 0 | free(*range_end_atom); |
689 | 0 | *range_end_atom = NULL; |
690 | 0 | } |
691 | 0 | msg = ovsdb_error_to_string_free(error); |
692 | 0 | } |
693 | 0 | return msg; |
694 | 0 | } |
695 | | |
696 | | static bool |
697 | | string_needs_quotes(const char *s) |
698 | 0 | { |
699 | 0 | const char *p = s; |
700 | 0 | unsigned char c; |
701 | 0 | struct uuid uuid; |
702 | |
|
703 | 0 | c = *p++; |
704 | 0 | if (!isalpha(c) && c != '_') { |
705 | 0 | return true; |
706 | 0 | } |
707 | | |
708 | 0 | while ((c = *p++) != '\0') { |
709 | 0 | if (!isalpha(c) && !isdigit(c) && c != '_' && c != '-' && c != '.') { |
710 | 0 | return true; |
711 | 0 | } |
712 | 0 | } |
713 | | |
714 | 0 | if (!strcmp(s, "true") || !strcmp(s, "false")) { |
715 | 0 | return true; |
716 | 0 | } |
717 | | |
718 | 0 | if (uuid_from_string(&uuid, s)) { |
719 | 0 | return true; |
720 | 0 | } |
721 | | |
722 | 0 | return false; |
723 | 0 | } |
724 | | |
725 | | /* Appends 'atom' (which has the given 'type') to 'out', in a format acceptable |
726 | | * to ovsdb_atom_from_string(). */ |
727 | | void |
728 | | ovsdb_atom_to_string(const union ovsdb_atom *atom, enum ovsdb_atomic_type type, |
729 | | struct ds *out) |
730 | 0 | { |
731 | 0 | switch (type) { |
732 | 0 | case OVSDB_TYPE_VOID: |
733 | 0 | OVS_NOT_REACHED(); |
734 | | |
735 | 0 | case OVSDB_TYPE_INTEGER: |
736 | 0 | ds_put_format(out, "%"PRId64, atom->integer); |
737 | 0 | break; |
738 | | |
739 | 0 | case OVSDB_TYPE_REAL: |
740 | 0 | ds_put_format(out, "%.*g", DBL_DIG, atom->real); |
741 | 0 | break; |
742 | | |
743 | 0 | case OVSDB_TYPE_BOOLEAN: |
744 | 0 | ds_put_cstr(out, atom->boolean ? "true" : "false"); |
745 | 0 | break; |
746 | | |
747 | 0 | case OVSDB_TYPE_STRING: |
748 | 0 | if (string_needs_quotes(json_string(atom->s))) { |
749 | 0 | json_to_ds(atom->s, 0, out); |
750 | 0 | } else { |
751 | 0 | ds_put_cstr(out, json_string(atom->s)); |
752 | 0 | } |
753 | 0 | break; |
754 | | |
755 | 0 | case OVSDB_TYPE_UUID: |
756 | 0 | ds_put_format(out, UUID_FMT, UUID_ARGS(&atom->uuid)); |
757 | 0 | break; |
758 | | |
759 | 0 | case OVSDB_N_TYPES: |
760 | 0 | default: |
761 | 0 | OVS_NOT_REACHED(); |
762 | 0 | } |
763 | 0 | } |
764 | | |
765 | | /* Appends 'atom' (which has the given 'type') to 'out', in a bare string |
766 | | * format that cannot be parsed uniformly back into a datum but is easier for |
767 | | * shell scripts, etc., to deal with. */ |
768 | | void |
769 | | ovsdb_atom_to_bare(const union ovsdb_atom *atom, enum ovsdb_atomic_type type, |
770 | | struct ds *out) |
771 | 0 | { |
772 | 0 | if (type == OVSDB_TYPE_STRING) { |
773 | 0 | ds_put_cstr(out, json_string(atom->s)); |
774 | 0 | } else { |
775 | 0 | ovsdb_atom_to_string(atom, type, out); |
776 | 0 | } |
777 | 0 | } |
778 | | |
779 | | static struct ovsdb_error * |
780 | | check_string_constraints(const char *s, |
781 | | const struct ovsdb_string_constraints *c) |
782 | 0 | { |
783 | 0 | size_t n_chars; |
784 | 0 | char *msg; |
785 | |
|
786 | 0 | msg = utf8_validate(s, &n_chars); |
787 | 0 | if (msg) { |
788 | 0 | struct ovsdb_error *error; |
789 | |
|
790 | 0 | error = ovsdb_error("constraint violation", |
791 | 0 | "not a valid UTF-8 string: %s", msg); |
792 | 0 | free(msg); |
793 | 0 | return error; |
794 | 0 | } |
795 | | |
796 | 0 | if (n_chars < c->minLen) { |
797 | 0 | return ovsdb_error( |
798 | 0 | "constraint violation", |
799 | 0 | "\"%s\" length %"PRIuSIZE" is less than minimum allowed " |
800 | 0 | "length %u", s, n_chars, c->minLen); |
801 | 0 | } else if (n_chars > c->maxLen) { |
802 | 0 | return ovsdb_error( |
803 | 0 | "constraint violation", |
804 | 0 | "\"%s\" length %"PRIuSIZE" is greater than maximum allowed " |
805 | 0 | "length %u", s, n_chars, c->maxLen); |
806 | 0 | } |
807 | | |
808 | 0 | return NULL; |
809 | 0 | } |
810 | | |
811 | | /* Checks whether 'atom' meets the constraints (if any) defined in 'base'. |
812 | | * (base->type must specify 'atom''s type.) Returns a null pointer if the |
813 | | * constraints are met, otherwise an error that explains the violation. |
814 | | * |
815 | | * Checking UUID constraints is deferred to transaction commit time, so this |
816 | | * function does nothing for UUID constraints. */ |
817 | | struct ovsdb_error * |
818 | | ovsdb_atom_check_constraints(const union ovsdb_atom *atom, |
819 | | const struct ovsdb_base_type *base) |
820 | 0 | { |
821 | 0 | if (base->enum_ |
822 | 0 | && !ovsdb_datum_find_key(base->enum_, atom, base->type, NULL)) { |
823 | 0 | struct ovsdb_error *error; |
824 | 0 | struct ds actual = DS_EMPTY_INITIALIZER; |
825 | 0 | struct ds valid = DS_EMPTY_INITIALIZER; |
826 | |
|
827 | 0 | ovsdb_atom_to_string(atom, base->type, &actual); |
828 | 0 | ovsdb_datum_to_string(base->enum_, |
829 | 0 | ovsdb_base_type_get_enum_type(base->type), |
830 | 0 | &valid); |
831 | 0 | error = ovsdb_error("constraint violation", |
832 | 0 | "%s is not one of the allowed values (%s)", |
833 | 0 | ds_cstr(&actual), ds_cstr(&valid)); |
834 | 0 | ds_destroy(&actual); |
835 | 0 | ds_destroy(&valid); |
836 | |
|
837 | 0 | return error; |
838 | 0 | } |
839 | | |
840 | 0 | switch (base->type) { |
841 | 0 | case OVSDB_TYPE_VOID: |
842 | 0 | OVS_NOT_REACHED(); |
843 | | |
844 | 0 | case OVSDB_TYPE_INTEGER: |
845 | 0 | if (atom->integer >= base->integer.min |
846 | 0 | && atom->integer <= base->integer.max) { |
847 | 0 | return NULL; |
848 | 0 | } else if (base->integer.min != INT64_MIN) { |
849 | 0 | if (base->integer.max != INT64_MAX) { |
850 | 0 | return ovsdb_error("constraint violation", |
851 | 0 | "%"PRId64" is not in the valid range " |
852 | 0 | "%"PRId64" to %"PRId64" (inclusive)", |
853 | 0 | atom->integer, |
854 | 0 | base->integer.min, base->integer.max); |
855 | 0 | } else { |
856 | 0 | return ovsdb_error("constraint violation", |
857 | 0 | "%"PRId64" is less than minimum allowed " |
858 | 0 | "value %"PRId64, |
859 | 0 | atom->integer, base->integer.min); |
860 | 0 | } |
861 | 0 | } else { |
862 | 0 | return ovsdb_error("constraint violation", |
863 | 0 | "%"PRId64" is greater than maximum allowed " |
864 | 0 | "value %"PRId64, |
865 | 0 | atom->integer, base->integer.max); |
866 | 0 | } |
867 | 0 | OVS_NOT_REACHED(); |
868 | | |
869 | 0 | case OVSDB_TYPE_REAL: |
870 | 0 | if (atom->real >= base->real.min && atom->real <= base->real.max) { |
871 | 0 | return NULL; |
872 | 0 | } else if (base->real.min != -DBL_MAX) { |
873 | 0 | if (base->real.max != DBL_MAX) { |
874 | 0 | return ovsdb_error("constraint violation", |
875 | 0 | "%.*g is not in the valid range " |
876 | 0 | "%.*g to %.*g (inclusive)", |
877 | 0 | DBL_DIG, atom->real, |
878 | 0 | DBL_DIG, base->real.min, |
879 | 0 | DBL_DIG, base->real.max); |
880 | 0 | } else { |
881 | 0 | return ovsdb_error("constraint violation", |
882 | 0 | "%.*g is less than minimum allowed " |
883 | 0 | "value %.*g", |
884 | 0 | DBL_DIG, atom->real, |
885 | 0 | DBL_DIG, base->real.min); |
886 | 0 | } |
887 | 0 | } else { |
888 | 0 | return ovsdb_error("constraint violation", |
889 | 0 | "%.*g is greater than maximum allowed " |
890 | 0 | "value %.*g", |
891 | 0 | DBL_DIG, atom->real, |
892 | 0 | DBL_DIG, base->real.max); |
893 | 0 | } |
894 | 0 | OVS_NOT_REACHED(); |
895 | | |
896 | 0 | case OVSDB_TYPE_BOOLEAN: |
897 | 0 | return NULL; |
898 | | |
899 | 0 | case OVSDB_TYPE_STRING: |
900 | 0 | return check_string_constraints(json_string(atom->s), &base->string); |
901 | | |
902 | 0 | case OVSDB_TYPE_UUID: |
903 | 0 | return NULL; |
904 | | |
905 | 0 | case OVSDB_N_TYPES: |
906 | 0 | default: |
907 | 0 | OVS_NOT_REACHED(); |
908 | 0 | } |
909 | 0 | } |
910 | | |
911 | | /* Initializes 'datum' as an empty datum. (An empty datum can be treated as |
912 | | * any type.) */ |
913 | | void |
914 | | ovsdb_datum_init_empty(struct ovsdb_datum *datum) |
915 | 0 | { |
916 | 0 | datum->n = 0; |
917 | 0 | datum->keys = NULL; |
918 | 0 | datum->values = NULL; |
919 | 0 | datum->refcnt = NULL; |
920 | 0 | } |
921 | | |
922 | | /* Initializes 'datum' as a datum that has the default value for 'type'. |
923 | | * |
924 | | * The default value for a particular type is as defined in RFC 7047: |
925 | | * |
926 | | * - If n_min is 0, then the default value is the empty set (or map). |
927 | | * |
928 | | * - If n_min is 1, the default value is a single value or a single |
929 | | * key-value pair, whose key and value are the defaults for their |
930 | | * atomic types. (See ovsdb_atom_init_default() for details.) |
931 | | * |
932 | | * - n_min > 1 is invalid. See ovsdb_type_is_valid(). |
933 | | */ |
934 | | void |
935 | | ovsdb_datum_init_default(struct ovsdb_datum *datum, |
936 | | const struct ovsdb_type *type) |
937 | 0 | { |
938 | 0 | datum->n = type->n_min; |
939 | 0 | datum->keys = alloc_default_atoms(type->key.type, datum->n); |
940 | 0 | datum->values = alloc_default_atoms(type->value.type, datum->n); |
941 | 0 | datum->refcnt = NULL; |
942 | 0 | } |
943 | | |
944 | | /* Returns a read-only datum of the given 'type' that has the default value for |
945 | | * 'type'. The caller must not modify or free the returned datum. |
946 | | * |
947 | | * See ovsdb_datum_init_default() for an explanation of the default value of a |
948 | | * datum. */ |
949 | | const struct ovsdb_datum * |
950 | | ovsdb_datum_default(const struct ovsdb_type *type) |
951 | 0 | { |
952 | 0 | if (type->n_min == 0) { |
953 | 0 | static unsigned int refcnt = 1; |
954 | 0 | static const struct ovsdb_datum empty = { .refcnt = &refcnt }; |
955 | 0 | return ∅ |
956 | 0 | } else if (type->n_min == 1) { |
957 | 0 | static struct ovsdb_datum default_data[OVSDB_N_TYPES][OVSDB_N_TYPES]; |
958 | 0 | static unsigned int refcnt[OVSDB_N_TYPES][OVSDB_N_TYPES]; |
959 | 0 | struct ovsdb_datum *d; |
960 | 0 | int kt = type->key.type; |
961 | 0 | int vt = type->value.type; |
962 | |
|
963 | 0 | ovs_assert(ovsdb_type_is_valid(type)); |
964 | |
|
965 | 0 | d = &default_data[kt][vt]; |
966 | 0 | if (!d->n) { |
967 | 0 | d->n = 1; |
968 | 0 | d->keys = CONST_CAST(union ovsdb_atom *, ovsdb_atom_default(kt)); |
969 | 0 | if (vt != OVSDB_TYPE_VOID) { |
970 | 0 | d->values = CONST_CAST(union ovsdb_atom *, |
971 | 0 | ovsdb_atom_default(vt)); |
972 | 0 | } |
973 | 0 | d->refcnt = &refcnt[kt][vt]; |
974 | 0 | *d->refcnt = 1; |
975 | 0 | } |
976 | 0 | return d; |
977 | 0 | } else { |
978 | 0 | OVS_NOT_REACHED(); |
979 | 0 | } |
980 | 0 | } |
981 | | |
982 | | /* Returns true if 'datum', which must have the given 'type', has the default |
983 | | * value for that type. |
984 | | * |
985 | | * See ovsdb_datum_init_default() for an explanation of the default value of a |
986 | | * datum. */ |
987 | | bool |
988 | | ovsdb_datum_is_default(const struct ovsdb_datum *datum, |
989 | | const struct ovsdb_type *type) |
990 | 0 | { |
991 | 0 | size_t i; |
992 | |
|
993 | 0 | if (datum->n != type->n_min) { |
994 | 0 | return false; |
995 | 0 | } |
996 | 0 | for (i = 0; i < datum->n; i++) { |
997 | 0 | if (!ovsdb_atom_is_default(&datum->keys[i], type->key.type)) { |
998 | 0 | return false; |
999 | 0 | } |
1000 | 0 | if (type->value.type != OVSDB_TYPE_VOID |
1001 | 0 | && !ovsdb_atom_is_default(&datum->values[i], type->value.type)) { |
1002 | 0 | return false; |
1003 | 0 | } |
1004 | 0 | } |
1005 | | |
1006 | 0 | return true; |
1007 | 0 | } |
1008 | | |
1009 | | static union ovsdb_atom * |
1010 | | clone_atoms(const union ovsdb_atom *old, enum ovsdb_atomic_type type, size_t n) |
1011 | 0 | { |
1012 | 0 | if (type != OVSDB_TYPE_VOID && n) { |
1013 | 0 | union ovsdb_atom *new; |
1014 | 0 | unsigned int i; |
1015 | |
|
1016 | 0 | new = xmalloc(n * sizeof *new); |
1017 | 0 | for (i = 0; i < n; i++) { |
1018 | 0 | ovsdb_atom_clone(&new[i], &old[i], type); |
1019 | 0 | } |
1020 | 0 | return new; |
1021 | 0 | } else { |
1022 | | /* Avoid wasting memory in the n == 0 case, because xmalloc(0) is |
1023 | | * treated as xmalloc(1). */ |
1024 | 0 | return NULL; |
1025 | 0 | } |
1026 | 0 | } |
1027 | | |
1028 | | /* Initializes 'new' as a shallow copy of 'old_'. |
1029 | | * |
1030 | | * The caller must eventually arrange for 'new' to be destroyed (with |
1031 | | * ovsdb_datum_destroy()). The caller must call ovsdb_datum_unshare() |
1032 | | * before attempting direct modifications of the 'new' or 'old_', i.e. |
1033 | | * modifications outside of the ovsdb_datum_* API. */ |
1034 | | void |
1035 | | ovsdb_datum_clone(struct ovsdb_datum *new, const struct ovsdb_datum *old_) |
1036 | 0 | { |
1037 | 0 | struct ovsdb_datum *old = CONST_CAST(struct ovsdb_datum *, old_); |
1038 | |
|
1039 | 0 | if (!old->refcnt) { |
1040 | 0 | old->refcnt = xmalloc(sizeof *old->refcnt); |
1041 | 0 | *old->refcnt = 1; |
1042 | 0 | } |
1043 | 0 | memcpy(new, old, sizeof *new); |
1044 | 0 | (*new->refcnt)++; |
1045 | 0 | } |
1046 | | |
1047 | | static void |
1048 | | free_data(enum ovsdb_atomic_type type, |
1049 | | union ovsdb_atom *atoms, size_t n_atoms) |
1050 | 0 | { |
1051 | 0 | if (!atoms) { |
1052 | 0 | return; |
1053 | 0 | } |
1054 | | |
1055 | 0 | if (ovsdb_atom_needs_destruction(type)) { |
1056 | 0 | unsigned int i; |
1057 | 0 | for (i = 0; i < n_atoms; i++) { |
1058 | 0 | ovsdb_atom_destroy(&atoms[i], type); |
1059 | 0 | } |
1060 | 0 | } |
1061 | 0 | free(atoms); |
1062 | 0 | } |
1063 | | |
1064 | | /* Frees the data owned by 'datum', which must have the given 'type'. |
1065 | | * |
1066 | | * This does not actually call free(datum). If necessary, the caller must be |
1067 | | * responsible for that. */ |
1068 | | void |
1069 | | ovsdb_datum_destroy(struct ovsdb_datum *datum, const struct ovsdb_type *type) |
1070 | 0 | { |
1071 | 0 | if (!datum->refcnt || !--(*datum->refcnt)) { |
1072 | 0 | free_data(type->key.type, datum->keys, datum->n); |
1073 | 0 | free_data(type->value.type, datum->values, datum->n); |
1074 | 0 | free(datum->refcnt); |
1075 | 0 | } |
1076 | 0 | } |
1077 | | |
1078 | | /* This function should be called before attempting direct modifications |
1079 | | * of the 'datum', i.e. modifications outside of the ovsdb_datum_* API. */ |
1080 | | void |
1081 | | ovsdb_datum_unshare(struct ovsdb_datum *datum, const struct ovsdb_type *type) |
1082 | 0 | { |
1083 | 0 | if (!datum->refcnt || *datum->refcnt == 1) { |
1084 | 0 | return; |
1085 | 0 | } |
1086 | 0 | datum->keys = clone_atoms(datum->keys, type->key.type, datum->n); |
1087 | 0 | datum->values = clone_atoms(datum->values, type->value.type, datum->n); |
1088 | 0 | (*datum->refcnt)--; |
1089 | 0 | datum->refcnt = NULL; |
1090 | 0 | } |
1091 | | |
1092 | | /* Swaps the contents of 'a' and 'b', which need not have the same type. */ |
1093 | | void |
1094 | | ovsdb_datum_swap(struct ovsdb_datum *a, struct ovsdb_datum *b) |
1095 | 0 | { |
1096 | 0 | struct ovsdb_datum tmp = *a; |
1097 | 0 | *a = *b; |
1098 | 0 | *b = tmp; |
1099 | 0 | } |
1100 | | |
1101 | | struct ovsdb_datum_sort_cbdata { |
1102 | | enum ovsdb_atomic_type key_type; |
1103 | | enum ovsdb_atomic_type value_type; |
1104 | | struct ovsdb_datum *datum; |
1105 | | }; |
1106 | | |
1107 | | static int |
1108 | | ovsdb_datum_sort_compare_cb(size_t a, size_t b, void *cbdata_) |
1109 | 0 | { |
1110 | 0 | struct ovsdb_datum_sort_cbdata *cbdata = cbdata_; |
1111 | 0 | int retval; |
1112 | |
|
1113 | 0 | retval = ovsdb_atom_compare_3way(&cbdata->datum->keys[a], |
1114 | 0 | &cbdata->datum->keys[b], |
1115 | 0 | cbdata->key_type); |
1116 | 0 | if (retval || cbdata->value_type == OVSDB_TYPE_VOID) { |
1117 | 0 | return retval; |
1118 | 0 | } |
1119 | | |
1120 | 0 | return ovsdb_atom_compare_3way(&cbdata->datum->values[a], |
1121 | 0 | &cbdata->datum->values[b], |
1122 | 0 | cbdata->value_type); |
1123 | 0 | } |
1124 | | |
1125 | | static void |
1126 | | ovsdb_datum_sort_swap_cb(size_t a, size_t b, void *cbdata_) |
1127 | 0 | { |
1128 | 0 | struct ovsdb_datum_sort_cbdata *cbdata = cbdata_; |
1129 | |
|
1130 | 0 | ovsdb_atom_swap(&cbdata->datum->keys[a], &cbdata->datum->keys[b]); |
1131 | 0 | if (cbdata->datum->values) { |
1132 | 0 | ovsdb_atom_swap(&cbdata->datum->values[a], &cbdata->datum->values[b]); |
1133 | 0 | } |
1134 | 0 | } |
1135 | | |
1136 | | static void |
1137 | | ovsdb_datum_sort__(struct ovsdb_datum *datum, enum ovsdb_atomic_type key_type, |
1138 | | enum ovsdb_atomic_type value_type) |
1139 | 0 | { |
1140 | 0 | struct ovsdb_datum_sort_cbdata cbdata; |
1141 | |
|
1142 | 0 | cbdata.key_type = key_type; |
1143 | 0 | cbdata.value_type = value_type; |
1144 | 0 | cbdata.datum = datum; |
1145 | 0 | sort(datum->n, ovsdb_datum_sort_compare_cb, ovsdb_datum_sort_swap_cb, |
1146 | 0 | &cbdata); |
1147 | 0 | } |
1148 | | |
1149 | | /* The keys in an ovsdb_datum must be unique and in sorted order. Most |
1150 | | * functions that modify an ovsdb_datum maintain these invariants. For those |
1151 | | * that don't, this function checks and restores these invariants for 'datum', |
1152 | | * whose keys are of type 'key_type'. |
1153 | | * |
1154 | | * This function returns NULL if successful, otherwise an error message. The |
1155 | | * caller must free the returned error when it is no longer needed. On error, |
1156 | | * 'datum' is sorted but not unique. */ |
1157 | | struct ovsdb_error * |
1158 | | ovsdb_datum_sort(struct ovsdb_datum *datum, const struct ovsdb_type *type) |
1159 | 0 | { |
1160 | 0 | size_t i; |
1161 | |
|
1162 | 0 | if (datum->n < 2) { |
1163 | 0 | return NULL; |
1164 | 0 | } |
1165 | | |
1166 | 0 | ovsdb_datum_unshare(datum, type); |
1167 | |
|
1168 | 0 | ovsdb_datum_sort__(datum, type->key.type, OVSDB_TYPE_VOID); |
1169 | |
|
1170 | 0 | for (i = 0; i < datum->n - 1; i++) { |
1171 | 0 | if (ovsdb_atom_equals(&datum->keys[i], &datum->keys[i + 1], |
1172 | 0 | type->key.type)) { |
1173 | 0 | if (datum->values) { |
1174 | 0 | return ovsdb_error(NULL, "map contains duplicate key"); |
1175 | 0 | } else { |
1176 | 0 | return ovsdb_error(NULL, "set contains duplicate"); |
1177 | 0 | } |
1178 | 0 | } |
1179 | 0 | } |
1180 | 0 | return NULL; |
1181 | 0 | } |
1182 | | |
1183 | | /* This function is the same as ovsdb_datum_sort(), except that the caller |
1184 | | * knows that 'datum' is unique. The operation therefore "cannot fail", so |
1185 | | * this function assert-fails if it actually does. */ |
1186 | | void |
1187 | | ovsdb_datum_sort_assert(struct ovsdb_datum *datum, |
1188 | | const struct ovsdb_type *type) |
1189 | 0 | { |
1190 | 0 | struct ovsdb_error *error = ovsdb_datum_sort(datum, type); |
1191 | 0 | if (error) { |
1192 | 0 | OVS_NOT_REACHED(); |
1193 | 0 | } |
1194 | 0 | } |
1195 | | |
1196 | | /* This is similar to ovsdb_datum_sort(), except that it drops duplicate keys |
1197 | | * instead of reporting an error. In a map type, the smallest value among a |
1198 | | * group of duplicate pairs is retained and the others are dropped. |
1199 | | * |
1200 | | * Returns the number of keys (or pairs) that were dropped. */ |
1201 | | size_t |
1202 | | ovsdb_datum_sort_unique(struct ovsdb_datum *datum, |
1203 | | const struct ovsdb_type *type) |
1204 | 0 | { |
1205 | 0 | size_t src, dst; |
1206 | |
|
1207 | 0 | if (datum->n < 2) { |
1208 | 0 | return 0; |
1209 | 0 | } |
1210 | | |
1211 | 0 | ovsdb_datum_unshare(datum, type); |
1212 | 0 | ovsdb_datum_sort__(datum, type->key.type, type->value.type); |
1213 | |
|
1214 | 0 | dst = 1; |
1215 | 0 | for (src = 1; src < datum->n; src++) { |
1216 | 0 | if (ovsdb_atom_equals(&datum->keys[src], &datum->keys[dst - 1], |
1217 | 0 | type->key.type)) { |
1218 | 0 | ovsdb_atom_destroy(&datum->keys[src], type->key.type); |
1219 | 0 | if (type->value.type != OVSDB_TYPE_VOID) { |
1220 | 0 | ovsdb_atom_destroy(&datum->values[src], type->value.type); |
1221 | 0 | } |
1222 | 0 | } else { |
1223 | 0 | if (src != dst) { |
1224 | 0 | datum->keys[dst] = datum->keys[src]; |
1225 | 0 | if (type->value.type != OVSDB_TYPE_VOID) { |
1226 | 0 | datum->values[dst] = datum->values[src]; |
1227 | 0 | } |
1228 | 0 | } |
1229 | 0 | dst++; |
1230 | 0 | } |
1231 | 0 | } |
1232 | 0 | datum->n = dst; |
1233 | 0 | return datum->n - src; |
1234 | 0 | } |
1235 | | |
1236 | | /* Checks that each of the atoms in 'datum' conforms to the constraints |
1237 | | * specified by its 'type'. Returns an error if a constraint is violated, |
1238 | | * otherwise a null pointer. |
1239 | | * |
1240 | | * This function is not commonly useful because the most ordinary way to obtain |
1241 | | * a datum is ultimately via ovsdb_atom_from_string() or |
1242 | | * ovsdb_atom_from_json(), which check constraints themselves. */ |
1243 | | struct ovsdb_error * |
1244 | | ovsdb_datum_check_constraints(const struct ovsdb_datum *datum, |
1245 | | const struct ovsdb_type *type) |
1246 | 0 | { |
1247 | 0 | struct ovsdb_error *error; |
1248 | 0 | unsigned int i; |
1249 | |
|
1250 | 0 | for (i = 0; i < datum->n; i++) { |
1251 | 0 | error = ovsdb_atom_check_constraints(&datum->keys[i], &type->key); |
1252 | 0 | if (error) { |
1253 | 0 | return error; |
1254 | 0 | } |
1255 | 0 | } |
1256 | | |
1257 | 0 | if (type->value.type != OVSDB_TYPE_VOID) { |
1258 | 0 | for (i = 0; i < datum->n; i++) { |
1259 | 0 | error = ovsdb_atom_check_constraints(&datum->values[i], |
1260 | 0 | &type->value); |
1261 | 0 | if (error) { |
1262 | 0 | return error; |
1263 | 0 | } |
1264 | 0 | } |
1265 | 0 | } |
1266 | | |
1267 | 0 | return NULL; |
1268 | 0 | } |
1269 | | |
1270 | | static struct ovsdb_error * |
1271 | | ovsdb_datum_from_json__(struct ovsdb_datum *datum, |
1272 | | const struct ovsdb_type *type, |
1273 | | const struct json *json, |
1274 | | struct ovsdb_symbol_table *symtab) |
1275 | 0 | { |
1276 | 0 | struct ovsdb_error *error; |
1277 | |
|
1278 | 0 | if (ovsdb_type_is_map(type) |
1279 | 0 | || (json->type == JSON_ARRAY |
1280 | 0 | && json->array.n > 0 |
1281 | 0 | && json->array.elems[0]->type == JSON_STRING |
1282 | 0 | && !strcmp(json->array.elems[0]->string, "set"))) { |
1283 | 0 | bool is_map = ovsdb_type_is_map(type); |
1284 | 0 | const char *class = is_map ? "map" : "set"; |
1285 | 0 | const struct json *inner; |
1286 | 0 | unsigned int i; |
1287 | 0 | size_t n; |
1288 | |
|
1289 | 0 | error = unwrap_json(json, class, JSON_ARRAY, &inner); |
1290 | 0 | if (error) { |
1291 | 0 | return error; |
1292 | 0 | } |
1293 | | |
1294 | 0 | n = inner->array.n; |
1295 | 0 | if (n < type->n_min || n > type->n_max) { |
1296 | 0 | if (type->n_min == 1 && type->n_max == 1) { |
1297 | 0 | return ovsdb_syntax_error(json, NULL, "%s must have exactly " |
1298 | 0 | "one member but %"PRIuSIZE" " |
1299 | 0 | "are present", class, n); |
1300 | 0 | } else { |
1301 | 0 | return ovsdb_syntax_error(json, NULL, "%s must have %u to " |
1302 | 0 | "%u members but %"PRIuSIZE" are " |
1303 | 0 | "present", |
1304 | 0 | class, type->n_min, type->n_max, n); |
1305 | 0 | } |
1306 | 0 | } |
1307 | | |
1308 | 0 | datum->n = 0; |
1309 | 0 | datum->keys = xmalloc(n * sizeof *datum->keys); |
1310 | 0 | datum->values = is_map ? xmalloc(n * sizeof *datum->values) : NULL; |
1311 | 0 | datum->refcnt = NULL; |
1312 | 0 | for (i = 0; i < n; i++) { |
1313 | 0 | const struct json *element = inner->array.elems[i]; |
1314 | 0 | const struct json *key = NULL; |
1315 | 0 | const struct json *value = NULL; |
1316 | |
|
1317 | 0 | if (!is_map) { |
1318 | 0 | key = element; |
1319 | 0 | } else { |
1320 | 0 | error = parse_json_pair(element, &key, &value); |
1321 | 0 | if (error) { |
1322 | 0 | goto error; |
1323 | 0 | } |
1324 | 0 | } |
1325 | | |
1326 | 0 | error = ovsdb_atom_from_json(&datum->keys[i], &type->key, |
1327 | 0 | key, symtab); |
1328 | 0 | if (error) { |
1329 | 0 | goto error; |
1330 | 0 | } |
1331 | | |
1332 | 0 | if (is_map) { |
1333 | 0 | error = ovsdb_atom_from_json(&datum->values[i], |
1334 | 0 | &type->value, value, symtab); |
1335 | 0 | if (error) { |
1336 | 0 | ovsdb_atom_destroy(&datum->keys[i], type->key.type); |
1337 | 0 | goto error; |
1338 | 0 | } |
1339 | 0 | } |
1340 | | |
1341 | 0 | datum->n++; |
1342 | 0 | } |
1343 | 0 | return NULL; |
1344 | | |
1345 | 0 | error: |
1346 | 0 | ovsdb_datum_destroy(datum, type); |
1347 | 0 | return error; |
1348 | 0 | } else { |
1349 | 0 | datum->n = 1; |
1350 | 0 | datum->keys = xmalloc(sizeof *datum->keys); |
1351 | 0 | datum->values = NULL; |
1352 | 0 | datum->refcnt = NULL; |
1353 | |
|
1354 | 0 | error = ovsdb_atom_from_json(&datum->keys[0], &type->key, |
1355 | 0 | json, symtab); |
1356 | 0 | if (error) { |
1357 | 0 | free(datum->keys); |
1358 | 0 | } |
1359 | 0 | return error; |
1360 | 0 | } |
1361 | 0 | } |
1362 | | |
1363 | | /* Parses 'json' as a datum of the type described by 'type'. If successful, |
1364 | | * returns NULL and initializes 'datum' with the parsed datum. On failure, |
1365 | | * returns an error and the contents of 'datum' are indeterminate. The caller |
1366 | | * is responsible for freeing the error or the datum that is returned. |
1367 | | * |
1368 | | * Violations of constraints expressed by 'type' are treated as errors. |
1369 | | * |
1370 | | * If 'symtab' is nonnull, then named UUIDs in 'symtab' are accepted. Refer to |
1371 | | * RFC 7047 for information about this, and for the syntax that this function |
1372 | | * accepts. */ |
1373 | | struct ovsdb_error * OVS_WARN_UNUSED_RESULT |
1374 | | ovsdb_datum_from_json(struct ovsdb_datum *datum, |
1375 | | const struct ovsdb_type *type, |
1376 | | const struct json *json, |
1377 | | struct ovsdb_symbol_table *symtab) |
1378 | 0 | { |
1379 | 0 | struct ovsdb_error *error; |
1380 | |
|
1381 | 0 | error = ovsdb_datum_from_json__(datum, type, json, symtab); |
1382 | 0 | if (error) { |
1383 | 0 | return error; |
1384 | 0 | } |
1385 | | |
1386 | 0 | error = ovsdb_datum_sort(datum, type); |
1387 | 0 | if (error) { |
1388 | 0 | ovsdb_datum_destroy(datum, type); |
1389 | 0 | } |
1390 | 0 | return error; |
1391 | 0 | } |
1392 | | |
1393 | | /* Parses 'json' as a datum of the type described by 'type' for internal |
1394 | | * use. This function is similar to 'ovsdb_datum_from_json', except the |
1395 | | * member size of set or map is not checked. |
1396 | | * |
1397 | | * The datum generated should be used then discard. It is not suitable |
1398 | | * for storing into IDL because of the possible member size violation. */ |
1399 | | struct ovsdb_error * OVS_WARN_UNUSED_RESULT |
1400 | | ovsdb_transient_datum_from_json(struct ovsdb_datum *datum, |
1401 | | const struct ovsdb_type *type, |
1402 | | const struct json *json) |
1403 | 0 | { |
1404 | 0 | struct ovsdb_type relaxed_type = *type; |
1405 | |
|
1406 | 0 | relaxed_type.n_min = 0; |
1407 | 0 | relaxed_type.n_max = UINT_MAX; |
1408 | |
|
1409 | 0 | return ovsdb_datum_from_json(datum, &relaxed_type, json, NULL); |
1410 | 0 | } |
1411 | | |
1412 | | /* Parses 'json' as a datum of the type described by 'type', but ignoring all |
1413 | | * constraints. */ |
1414 | | struct ovsdb_error * OVS_WARN_UNUSED_RESULT |
1415 | | ovsdb_unconstrained_datum_from_json(struct ovsdb_datum *datum, |
1416 | | const struct ovsdb_type *type, |
1417 | | const struct json *json) |
1418 | 0 | { |
1419 | 0 | struct ovsdb_type relaxed_type; |
1420 | |
|
1421 | 0 | ovsdb_base_type_init(&relaxed_type.key, type->key.type); |
1422 | 0 | ovsdb_base_type_init(&relaxed_type.value, type->value.type); |
1423 | 0 | relaxed_type.n_min = 0; |
1424 | 0 | relaxed_type.n_max = UINT_MAX; |
1425 | |
|
1426 | 0 | return ovsdb_datum_from_json(datum, &relaxed_type, json, NULL); |
1427 | 0 | } |
1428 | | |
1429 | | static struct json * |
1430 | | ovsdb_base_to_json(const union ovsdb_atom *atom, |
1431 | | const struct ovsdb_base_type *base, |
1432 | | bool use_row_names, |
1433 | | bool allow_shallow_copies) |
1434 | 0 | { |
1435 | 0 | if (!use_row_names |
1436 | 0 | || base->type != OVSDB_TYPE_UUID |
1437 | 0 | || !base->uuid.refTableName) { |
1438 | 0 | return allow_shallow_copies |
1439 | 0 | ? ovsdb_atom_to_json(atom, base->type) |
1440 | 0 | : ovsdb_atom_to_json_deep(atom, base->type); |
1441 | 0 | } else { |
1442 | 0 | return json_array_create_2( |
1443 | 0 | json_string_create("named-uuid"), |
1444 | 0 | json_string_create_nocopy(ovsdb_data_row_name(&atom->uuid))); |
1445 | 0 | } |
1446 | 0 | } |
1447 | | |
1448 | | static struct json * |
1449 | | ovsdb_datum_to_json__(const struct ovsdb_datum *datum, |
1450 | | const struct ovsdb_type *type, |
1451 | | bool use_row_names, |
1452 | | bool allow_shallow_copies) |
1453 | 0 | { |
1454 | 0 | if (ovsdb_type_is_map(type)) { |
1455 | 0 | struct json **elems; |
1456 | 0 | size_t i; |
1457 | |
|
1458 | 0 | elems = xmalloc(datum->n * sizeof *elems); |
1459 | 0 | for (i = 0; i < datum->n; i++) { |
1460 | 0 | elems[i] = json_array_create_2( |
1461 | 0 | ovsdb_base_to_json(&datum->keys[i], &type->key, |
1462 | 0 | use_row_names, allow_shallow_copies), |
1463 | 0 | ovsdb_base_to_json(&datum->values[i], &type->value, |
1464 | 0 | use_row_names, allow_shallow_copies)); |
1465 | 0 | } |
1466 | |
|
1467 | 0 | return wrap_json("map", json_array_create(elems, datum->n)); |
1468 | 0 | } else if (datum->n == 1) { |
1469 | 0 | return ovsdb_base_to_json(&datum->keys[0], &type->key, |
1470 | 0 | use_row_names, allow_shallow_copies); |
1471 | 0 | } else { |
1472 | 0 | struct json **elems; |
1473 | 0 | size_t i; |
1474 | |
|
1475 | 0 | elems = xmalloc(datum->n * sizeof *elems); |
1476 | 0 | for (i = 0; i < datum->n; i++) { |
1477 | 0 | elems[i] = ovsdb_base_to_json(&datum->keys[i], &type->key, |
1478 | 0 | use_row_names, allow_shallow_copies); |
1479 | 0 | } |
1480 | |
|
1481 | 0 | return wrap_json("set", json_array_create(elems, datum->n)); |
1482 | 0 | } |
1483 | 0 | } |
1484 | | |
1485 | | /* Converts 'datum', of the specified 'type', to JSON format, and returns the |
1486 | | * JSON. The caller is responsible for freeing the returned JSON. |
1487 | | * |
1488 | | * 'type' constraints on datum->n are ignored. |
1489 | | * |
1490 | | * Refer to RFC 7047 for the format of the JSON that this function produces. */ |
1491 | | struct json * |
1492 | | ovsdb_datum_to_json(const struct ovsdb_datum *datum, |
1493 | | const struct ovsdb_type *type) |
1494 | 0 | { |
1495 | 0 | return ovsdb_datum_to_json__(datum, type, false, true); |
1496 | 0 | } |
1497 | | |
1498 | | struct json * |
1499 | | ovsdb_datum_to_json_deep(const struct ovsdb_datum *datum, |
1500 | | const struct ovsdb_type *type) |
1501 | 0 | { |
1502 | 0 | return ovsdb_datum_to_json__(datum, type, false, false); |
1503 | 0 | } |
1504 | | |
1505 | | struct json * |
1506 | | ovsdb_datum_to_json_with_row_names(const struct ovsdb_datum *datum, |
1507 | | const struct ovsdb_type *type) |
1508 | 0 | { |
1509 | 0 | return ovsdb_datum_to_json__(datum, type, true, true); |
1510 | 0 | } |
1511 | | |
1512 | | static const char * |
1513 | | skip_spaces(const char *p) |
1514 | 0 | { |
1515 | 0 | while (isspace((unsigned char) *p)) { |
1516 | 0 | p++; |
1517 | 0 | } |
1518 | 0 | return p; |
1519 | 0 | } |
1520 | | |
1521 | | static char * |
1522 | | parse_atom_token(const char **s, const struct ovsdb_base_type *base, |
1523 | | union ovsdb_atom *atom, union ovsdb_atom **range_end_atom, |
1524 | | struct ovsdb_symbol_table *symtab) |
1525 | 0 | { |
1526 | 0 | char *token, *error; |
1527 | |
|
1528 | 0 | error = ovsdb_token_parse(s, &token); |
1529 | 0 | if (!error) { |
1530 | 0 | error = ovsdb_atom_from_string(atom, range_end_atom, |
1531 | 0 | base, token, symtab); |
1532 | 0 | free(token); |
1533 | 0 | } |
1534 | 0 | return error; |
1535 | 0 | } |
1536 | | |
1537 | | static char * |
1538 | | parse_key_value(const char **s, const struct ovsdb_type *type, |
1539 | | union ovsdb_atom *key, union ovsdb_atom *value, |
1540 | | struct ovsdb_symbol_table *symtab, |
1541 | | union ovsdb_atom **range_end_key) |
1542 | 0 | { |
1543 | 0 | const char *start = *s; |
1544 | 0 | char *error; |
1545 | |
|
1546 | 0 | error = parse_atom_token(s, &type->key, key, range_end_key, symtab); |
1547 | |
|
1548 | 0 | if (!error && type->value.type != OVSDB_TYPE_VOID) { |
1549 | 0 | *s = skip_spaces(*s); |
1550 | 0 | if (**s == '=') { |
1551 | 0 | (*s)++; |
1552 | 0 | *s = skip_spaces(*s); |
1553 | 0 | error = parse_atom_token(s, &type->value, value, NULL, symtab); |
1554 | 0 | } else { |
1555 | 0 | error = xasprintf("%s: syntax error at \"%c\" expecting \"=\"", |
1556 | 0 | start, **s); |
1557 | 0 | } |
1558 | 0 | if (error) { |
1559 | 0 | ovsdb_atom_destroy(key, type->key.type); |
1560 | 0 | if (range_end_key && *range_end_key) { |
1561 | 0 | ovsdb_atom_destroy(*range_end_key, type->key.type); |
1562 | 0 | free(*range_end_key); |
1563 | 0 | *range_end_key = NULL; |
1564 | 0 | } |
1565 | 0 | } |
1566 | 0 | } |
1567 | 0 | return error; |
1568 | 0 | } |
1569 | | |
1570 | | static void |
1571 | | free_key_value_range(const struct ovsdb_type *type, |
1572 | | union ovsdb_atom *key, union ovsdb_atom *value, |
1573 | | union ovsdb_atom **range_end_atom) |
1574 | 0 | { |
1575 | 0 | ovsdb_atom_destroy(key, type->key.type); |
1576 | 0 | if (type->value.type != OVSDB_TYPE_VOID) { |
1577 | 0 | ovsdb_atom_destroy(value, type->value.type); |
1578 | 0 | } |
1579 | 0 | if (range_end_atom && *range_end_atom) { |
1580 | 0 | ovsdb_atom_destroy(*range_end_atom, type->key.type); |
1581 | 0 | free(*range_end_atom); |
1582 | 0 | *range_end_atom = NULL; |
1583 | 0 | } |
1584 | 0 | } |
1585 | | |
1586 | | /* Initializes 'datum' as a datum of the given 'type', parsing its contents |
1587 | | * from 's'. The format of 's' is a series of space or comma separated atoms |
1588 | | * or, for a map, '='-delimited pairs of atoms. Each atom must in a format |
1589 | | * acceptable to ovsdb_atom_from_string(). Optionally, a set may be enclosed |
1590 | | * in "[]" or a map in "{}"; for an empty set or map these punctuators are |
1591 | | * required. |
1592 | | * |
1593 | | * Optionally, a symbol table may be supplied as 'symtab'. It is passed to |
1594 | | * ovsdb_atom_to_string(). */ |
1595 | | char * |
1596 | | ovsdb_datum_from_string(struct ovsdb_datum *datum, |
1597 | | const struct ovsdb_type *type, const char *s, |
1598 | | struct ovsdb_symbol_table *symtab) |
1599 | 0 | { |
1600 | 0 | bool is_map = ovsdb_type_is_map(type); |
1601 | 0 | struct ovsdb_error *dberror; |
1602 | 0 | const char *p; |
1603 | 0 | int end_delim; |
1604 | 0 | char *error; |
1605 | |
|
1606 | 0 | ovsdb_datum_init_empty(datum); |
1607 | | |
1608 | | /* Swallow a leading delimiter if there is one. */ |
1609 | 0 | p = skip_spaces(s); |
1610 | 0 | if (*p == (is_map ? '{' : '[')) { |
1611 | 0 | end_delim = is_map ? '}' : ']'; |
1612 | 0 | p = skip_spaces(p + 1); |
1613 | 0 | } else if (!*p) { |
1614 | 0 | if (is_map) { |
1615 | 0 | return xstrdup("use \"{}\" to specify the empty map"); |
1616 | 0 | } else { |
1617 | 0 | return xstrdup("use \"[]\" to specify the empty set"); |
1618 | 0 | } |
1619 | 0 | } else { |
1620 | 0 | end_delim = 0; |
1621 | 0 | } |
1622 | | |
1623 | 0 | while (*p && *p != end_delim) { |
1624 | 0 | union ovsdb_atom key, value; |
1625 | 0 | union ovsdb_atom *range_end_key = NULL; |
1626 | |
|
1627 | 0 | if (ovsdb_token_is_delim(*p)) { |
1628 | 0 | char *type_str = ovsdb_type_to_english(type); |
1629 | 0 | error = xasprintf("%s: unexpected \"%c\" parsing %s", |
1630 | 0 | s, *p, type_str); |
1631 | 0 | free(type_str); |
1632 | 0 | goto error; |
1633 | 0 | } |
1634 | | |
1635 | | /* Add to datum. */ |
1636 | 0 | error = parse_key_value(&p, type, &key, &value, |
1637 | 0 | symtab, &range_end_key); |
1638 | 0 | if (error) { |
1639 | 0 | goto error; |
1640 | 0 | } |
1641 | 0 | ovsdb_datum_add_unsafe(datum, &key, &value, type, range_end_key); |
1642 | 0 | free_key_value_range(type, &key, &value, &range_end_key); |
1643 | | |
1644 | | /* Skip optional white space and comma. */ |
1645 | 0 | p = skip_spaces(p); |
1646 | 0 | if (*p == ',') { |
1647 | 0 | p = skip_spaces(p + 1); |
1648 | 0 | } |
1649 | 0 | } |
1650 | | |
1651 | 0 | if (*p != end_delim) { |
1652 | 0 | error = xasprintf("%s: missing \"%c\" at end of data", s, end_delim); |
1653 | 0 | goto error; |
1654 | 0 | } |
1655 | 0 | if (end_delim) { |
1656 | 0 | p = skip_spaces(p + 1); |
1657 | 0 | if (*p) { |
1658 | 0 | error = xasprintf("%s: trailing garbage after \"%c\"", |
1659 | 0 | s, end_delim); |
1660 | 0 | goto error; |
1661 | 0 | } |
1662 | 0 | } |
1663 | | |
1664 | 0 | if (datum->n < type->n_min) { |
1665 | 0 | error = xasprintf("%s: %u %s specified but the minimum number is %u", |
1666 | 0 | s, datum->n, is_map ? "pair(s)" : "value(s)", |
1667 | 0 | type->n_min); |
1668 | 0 | goto error; |
1669 | 0 | } else if (datum->n > type->n_max) { |
1670 | 0 | error = xasprintf("%s: %u %s specified but the maximum number is %u", |
1671 | 0 | s, datum->n, is_map ? "pair(s)" : "value(s)", |
1672 | 0 | type->n_max); |
1673 | 0 | goto error; |
1674 | 0 | } |
1675 | | |
1676 | 0 | dberror = ovsdb_datum_sort(datum, type); |
1677 | 0 | if (dberror) { |
1678 | 0 | ovsdb_error_destroy(dberror); |
1679 | 0 | if (ovsdb_type_is_map(type)) { |
1680 | 0 | error = xasprintf("%s: map contains duplicate key", s); |
1681 | 0 | } else { |
1682 | 0 | error = xasprintf("%s: set contains duplicate value", s); |
1683 | 0 | } |
1684 | 0 | goto error; |
1685 | 0 | } |
1686 | | |
1687 | 0 | return NULL; |
1688 | | |
1689 | 0 | error: |
1690 | 0 | ovsdb_datum_destroy(datum, type); |
1691 | 0 | ovsdb_datum_init_empty(datum); |
1692 | 0 | return error; |
1693 | 0 | } |
1694 | | |
1695 | | /* Appends to 'out' the 'datum' (with the given 'type') in a format acceptable |
1696 | | * to ovsdb_datum_from_string(). */ |
1697 | | void |
1698 | | ovsdb_datum_to_string(const struct ovsdb_datum *datum, |
1699 | | const struct ovsdb_type *type, struct ds *out) |
1700 | 0 | { |
1701 | 0 | bool is_map = ovsdb_type_is_map(type); |
1702 | 0 | size_t i; |
1703 | |
|
1704 | 0 | if (type->n_max > 1 || !datum->n) { |
1705 | 0 | ds_put_char(out, is_map ? '{' : '['); |
1706 | 0 | } |
1707 | 0 | for (i = 0; i < datum->n; i++) { |
1708 | 0 | if (i > 0) { |
1709 | 0 | ds_put_cstr(out, ", "); |
1710 | 0 | } |
1711 | |
|
1712 | 0 | ovsdb_atom_to_string(&datum->keys[i], type->key.type, out); |
1713 | 0 | if (is_map) { |
1714 | 0 | ds_put_char(out, '='); |
1715 | 0 | ovsdb_atom_to_string(&datum->values[i], type->value.type, out); |
1716 | 0 | } |
1717 | 0 | } |
1718 | 0 | if (type->n_max > 1 || !datum->n) { |
1719 | 0 | ds_put_char(out, is_map ? '}' : ']'); |
1720 | 0 | } |
1721 | 0 | } |
1722 | | |
1723 | | /* Appends to 'out' the 'datum' (with the given 'type') in a bare string format |
1724 | | * that cannot be parsed uniformly back into a datum but is easier for shell |
1725 | | * scripts, etc., to deal with. */ |
1726 | | void |
1727 | | ovsdb_datum_to_bare(const struct ovsdb_datum *datum, |
1728 | | const struct ovsdb_type *type, struct ds *out) |
1729 | 0 | { |
1730 | 0 | bool is_map = ovsdb_type_is_map(type); |
1731 | 0 | size_t i; |
1732 | |
|
1733 | 0 | for (i = 0; i < datum->n; i++) { |
1734 | 0 | if (i > 0) { |
1735 | 0 | ds_put_cstr(out, " "); |
1736 | 0 | } |
1737 | |
|
1738 | 0 | ovsdb_atom_to_bare(&datum->keys[i], type->key.type, out); |
1739 | 0 | if (is_map) { |
1740 | 0 | ds_put_char(out, '='); |
1741 | 0 | ovsdb_atom_to_bare(&datum->values[i], type->value.type, out); |
1742 | 0 | } |
1743 | 0 | } |
1744 | 0 | } |
1745 | | |
1746 | | /* Initializes 'datum' as a string-to-string map whose contents are copied from |
1747 | | * 'smap', which is not modified. */ |
1748 | | void |
1749 | | ovsdb_datum_from_smap(struct ovsdb_datum *datum, const struct smap *smap) |
1750 | 0 | { |
1751 | 0 | datum->n = smap_count(smap); |
1752 | 0 | datum->keys = xmalloc(datum->n * sizeof *datum->keys); |
1753 | 0 | datum->values = xmalloc(datum->n * sizeof *datum->values); |
1754 | 0 | datum->refcnt = NULL; |
1755 | |
|
1756 | 0 | struct smap_node *node; |
1757 | 0 | size_t i = 0; |
1758 | 0 | SMAP_FOR_EACH (node, smap) { |
1759 | 0 | datum->keys[i].s = ovsdb_atom_string_create(node->key); |
1760 | 0 | datum->values[i].s = ovsdb_atom_string_create(node->value); |
1761 | 0 | i++; |
1762 | 0 | } |
1763 | 0 | ovs_assert(i == datum->n); |
1764 | |
|
1765 | 0 | struct ovsdb_type type = { |
1766 | 0 | OVSDB_BASE_STRING_INIT, OVSDB_BASE_STRING_INIT, |
1767 | 0 | 0, UINT_MAX |
1768 | 0 | }; |
1769 | 0 | ovsdb_datum_sort_unique(datum, &type); |
1770 | 0 | } |
1771 | | |
1772 | | struct ovsdb_error * OVS_WARN_UNUSED_RESULT |
1773 | | ovsdb_datum_convert(struct ovsdb_datum *dst, |
1774 | | const struct ovsdb_type *dst_type, |
1775 | | const struct ovsdb_datum *src, |
1776 | | const struct ovsdb_type *src_type) |
1777 | 0 | { |
1778 | 0 | struct json *json = ovsdb_datum_to_json(src, src_type); |
1779 | 0 | struct ovsdb_error *error = ovsdb_datum_from_json(dst, dst_type, json, |
1780 | 0 | NULL); |
1781 | 0 | json_destroy(json); |
1782 | 0 | return error; |
1783 | 0 | } |
1784 | | |
1785 | | static uint32_t |
1786 | | hash_atoms(enum ovsdb_atomic_type type, const union ovsdb_atom *atoms, |
1787 | | unsigned int n, uint32_t basis) |
1788 | 0 | { |
1789 | 0 | if (type != OVSDB_TYPE_VOID) { |
1790 | 0 | unsigned int i; |
1791 | |
|
1792 | 0 | for (i = 0; i < n; i++) { |
1793 | 0 | basis = ovsdb_atom_hash(&atoms[i], type, basis); |
1794 | 0 | } |
1795 | 0 | } |
1796 | 0 | return basis; |
1797 | 0 | } |
1798 | | |
1799 | | uint32_t |
1800 | | ovsdb_datum_hash(const struct ovsdb_datum *datum, |
1801 | | const struct ovsdb_type *type, uint32_t basis) |
1802 | 0 | { |
1803 | 0 | basis = hash_atoms(type->key.type, datum->keys, datum->n, basis); |
1804 | 0 | basis ^= (type->key.type << 24) | (type->value.type << 16) | datum->n; |
1805 | 0 | basis = hash_atoms(type->value.type, datum->values, datum->n, basis); |
1806 | 0 | return basis; |
1807 | 0 | } |
1808 | | |
1809 | | static int |
1810 | | atom_arrays_compare_3way(const union ovsdb_atom *a, |
1811 | | const union ovsdb_atom *b, |
1812 | | enum ovsdb_atomic_type type, |
1813 | | size_t n) |
1814 | 0 | { |
1815 | 0 | unsigned int i; |
1816 | |
|
1817 | 0 | for (i = 0; i < n; i++) { |
1818 | 0 | int cmp = ovsdb_atom_compare_3way(&a[i], &b[i], type); |
1819 | 0 | if (cmp) { |
1820 | 0 | return cmp; |
1821 | 0 | } |
1822 | 0 | } |
1823 | | |
1824 | 0 | return 0; |
1825 | 0 | } |
1826 | | |
1827 | | bool |
1828 | | ovsdb_datum_equals(const struct ovsdb_datum *a, |
1829 | | const struct ovsdb_datum *b, |
1830 | | const struct ovsdb_type *type) |
1831 | 0 | { |
1832 | 0 | return !ovsdb_datum_compare_3way(a, b, type); |
1833 | 0 | } |
1834 | | |
1835 | | int |
1836 | | ovsdb_datum_compare_3way(const struct ovsdb_datum *a, |
1837 | | const struct ovsdb_datum *b, |
1838 | | const struct ovsdb_type *type) |
1839 | 0 | { |
1840 | 0 | int cmp; |
1841 | |
|
1842 | 0 | if (a->n != b->n) { |
1843 | 0 | return a->n < b->n ? -1 : 1; |
1844 | 0 | } |
1845 | | |
1846 | 0 | if (a->refcnt && a->refcnt == b->refcnt) { |
1847 | 0 | return 0; |
1848 | 0 | } |
1849 | | |
1850 | 0 | cmp = atom_arrays_compare_3way(a->keys, b->keys, type->key.type, a->n); |
1851 | 0 | if (cmp) { |
1852 | 0 | return cmp; |
1853 | 0 | } |
1854 | | |
1855 | 0 | return (type->value.type == OVSDB_TYPE_VOID ? 0 |
1856 | 0 | : atom_arrays_compare_3way(a->values, b->values, type->value.type, |
1857 | 0 | a->n)); |
1858 | 0 | } |
1859 | | |
1860 | | /* If 'key' is one of the keys in 'datum', returns 'true' and sets '*pos' to |
1861 | | * its index within 'datum', otherwise returns 'false' and sets '*pos' to the |
1862 | | * index where 'key' should have been. 'key.type' must be the type of the |
1863 | | * atoms stored in the 'keys' array in 'datum'. |
1864 | | */ |
1865 | | bool |
1866 | | ovsdb_datum_find_key(const struct ovsdb_datum *datum, |
1867 | | const union ovsdb_atom *key, |
1868 | | enum ovsdb_atomic_type key_type, |
1869 | | unsigned int *pos) |
1870 | 0 | { |
1871 | 0 | unsigned int low = 0; |
1872 | 0 | unsigned int high = datum->n; |
1873 | 0 | while (low < high) { |
1874 | 0 | unsigned int idx = (low + high) / 2; |
1875 | 0 | int cmp = ovsdb_atom_compare_3way(key, &datum->keys[idx], key_type); |
1876 | 0 | if (cmp < 0) { |
1877 | 0 | high = idx; |
1878 | 0 | } else if (cmp > 0) { |
1879 | 0 | low = idx + 1; |
1880 | 0 | } else { |
1881 | 0 | if (pos) { |
1882 | 0 | *pos = idx; |
1883 | 0 | } |
1884 | 0 | return true; |
1885 | 0 | } |
1886 | 0 | } |
1887 | 0 | if (pos) { |
1888 | 0 | *pos = low; |
1889 | 0 | } |
1890 | 0 | return false; |
1891 | 0 | } |
1892 | | |
1893 | | /* If 'key' and 'value' is one of the key-value pairs in 'datum', returns its |
1894 | | * index within 'datum', otherwise UINT_MAX. 'key.type' must be the type of |
1895 | | * the atoms stored in the 'keys' array in 'datum'. 'value_type' may be the |
1896 | | * type of the 'values' atoms or OVSDB_TYPE_VOID to compare only keys. |
1897 | | */ |
1898 | | unsigned int |
1899 | | ovsdb_datum_find_key_value(const struct ovsdb_datum *datum, |
1900 | | const union ovsdb_atom *key, |
1901 | | enum ovsdb_atomic_type key_type, |
1902 | | const union ovsdb_atom *value, |
1903 | | enum ovsdb_atomic_type value_type) |
1904 | 0 | { |
1905 | 0 | unsigned int idx; |
1906 | |
|
1907 | 0 | if (!ovsdb_datum_find_key(datum, key, key_type, &idx) |
1908 | 0 | || (value_type != OVSDB_TYPE_VOID |
1909 | 0 | && !ovsdb_atom_equals(&datum->values[idx], value, value_type))) { |
1910 | 0 | idx = UINT_MAX; |
1911 | 0 | } |
1912 | 0 | return idx; |
1913 | 0 | } |
1914 | | |
1915 | | /* If atom 'i' in 'a' is also in 'b', returns its index in 'b', otherwise |
1916 | | * UINT_MAX. 'type' must be the type of 'a' and 'b', except that |
1917 | | * type->value.type may be set to OVSDB_TYPE_VOID to compare keys but not |
1918 | | * values. */ |
1919 | | static unsigned int |
1920 | | ovsdb_datum_find(const struct ovsdb_datum *a, int i, |
1921 | | const struct ovsdb_datum *b, |
1922 | | const struct ovsdb_type *type) |
1923 | 0 | { |
1924 | 0 | return ovsdb_datum_find_key_value(b, |
1925 | 0 | &a->keys[i], type->key.type, |
1926 | 0 | a->values ? &a->values[i] : NULL, |
1927 | 0 | type->value.type); |
1928 | 0 | } |
1929 | | |
1930 | | /* Returns true if every element in 'a' is also in 'b', false otherwise. */ |
1931 | | bool |
1932 | | ovsdb_datum_includes_all(const struct ovsdb_datum *a, |
1933 | | const struct ovsdb_datum *b, |
1934 | | const struct ovsdb_type *type) |
1935 | 0 | { |
1936 | 0 | size_t i; |
1937 | |
|
1938 | 0 | if (a->n > b->n) { |
1939 | 0 | return false; |
1940 | 0 | } |
1941 | 0 | for (i = 0; i < a->n; i++) { |
1942 | 0 | if (ovsdb_datum_find(a, i, b, type) == UINT_MAX) { |
1943 | 0 | return false; |
1944 | 0 | } |
1945 | 0 | } |
1946 | 0 | return true; |
1947 | 0 | } |
1948 | | |
1949 | | /* Returns true if no element in 'a' is also in 'b', false otherwise. */ |
1950 | | bool |
1951 | | ovsdb_datum_excludes_all(const struct ovsdb_datum *a, |
1952 | | const struct ovsdb_datum *b, |
1953 | | const struct ovsdb_type *type) |
1954 | 0 | { |
1955 | 0 | size_t i; |
1956 | |
|
1957 | 0 | for (i = 0; i < a->n; i++) { |
1958 | 0 | if (ovsdb_datum_find(a, i, b, type) != UINT_MAX) { |
1959 | 0 | return false; |
1960 | 0 | } |
1961 | 0 | } |
1962 | 0 | return true; |
1963 | 0 | } |
1964 | | |
1965 | | static void |
1966 | | ovsdb_datum_reallocate(struct ovsdb_datum *a, const struct ovsdb_type *type, |
1967 | | unsigned int capacity) |
1968 | 0 | { |
1969 | 0 | ovsdb_datum_unshare(a, type); |
1970 | |
|
1971 | 0 | a->keys = xrealloc(a->keys, capacity * sizeof *a->keys); |
1972 | 0 | if (type->value.type != OVSDB_TYPE_VOID) { |
1973 | 0 | a->values = xrealloc(a->values, capacity * sizeof *a->values); |
1974 | 0 | } |
1975 | 0 | } |
1976 | | |
1977 | | /* Removes the element with index 'idx' from 'datum', which has type 'type'. |
1978 | | * If 'idx' is not the last element in 'datum', then the removed element is |
1979 | | * replaced by the (former) last element. |
1980 | | * |
1981 | | * This function does not maintain ovsdb_datum invariants. Use |
1982 | | * ovsdb_datum_sort() to check and restore these invariants. */ |
1983 | | void |
1984 | | ovsdb_datum_remove_unsafe(struct ovsdb_datum *datum, size_t idx, |
1985 | | const struct ovsdb_type *type) |
1986 | 0 | { |
1987 | 0 | ovsdb_datum_unshare(datum, type); |
1988 | |
|
1989 | 0 | ovsdb_atom_destroy(&datum->keys[idx], type->key.type); |
1990 | 0 | datum->keys[idx] = datum->keys[datum->n - 1]; |
1991 | 0 | if (type->value.type != OVSDB_TYPE_VOID) { |
1992 | 0 | ovsdb_atom_destroy(&datum->values[idx], type->value.type); |
1993 | 0 | datum->values[idx] = datum->values[datum->n - 1]; |
1994 | 0 | } |
1995 | 0 | datum->n--; |
1996 | 0 | } |
1997 | | |
1998 | | /* Adds the element with the given 'key' and 'value' to 'datum', which must |
1999 | | * have the specified 'type'. Optionally if 'range_end_atom' is not |
2000 | | * a null pointer, adds a set of integers to 'datum' from inclusive |
2001 | | * range ['key', 'range_end_atom']. |
2002 | | * |
2003 | | * This function always allocates memory, so it is not an efficient way to add |
2004 | | * a number of elements to a datum. |
2005 | | * |
2006 | | * When adding a range of integers, this function allocates the memory once |
2007 | | * for the whole range. |
2008 | | * |
2009 | | * This function does not maintain ovsdb_datum invariants. Use |
2010 | | * ovsdb_datum_sort() to check and restore these invariants. (But a datum with |
2011 | | * 0 or 1 elements cannot violate the invariants anyhow.) */ |
2012 | | void |
2013 | | ovsdb_datum_add_unsafe(struct ovsdb_datum *datum, |
2014 | | const union ovsdb_atom *key, |
2015 | | const union ovsdb_atom *value, |
2016 | | const struct ovsdb_type *type, |
2017 | | const union ovsdb_atom *range_end_atom) |
2018 | 0 | { |
2019 | 0 | size_t idx = datum->n; |
2020 | |
|
2021 | 0 | ovsdb_datum_unshare(datum, type); |
2022 | |
|
2023 | 0 | datum->n += range_end_atom ? |
2024 | 0 | (range_end_atom->integer - key->integer + 1) : 1; |
2025 | 0 | datum->keys = xrealloc(datum->keys, datum->n * sizeof *datum->keys); |
2026 | 0 | if (range_end_atom && key->integer <= range_end_atom->integer) { |
2027 | 0 | for (int64_t i = key->integer; i <= range_end_atom->integer; i++) { |
2028 | 0 | datum->keys[idx++].integer = i; |
2029 | 0 | } |
2030 | 0 | } else { |
2031 | 0 | ovsdb_atom_clone(&datum->keys[idx], key, type->key.type); |
2032 | 0 | if (type->value.type != OVSDB_TYPE_VOID) { |
2033 | 0 | datum->values = xrealloc(datum->values, |
2034 | 0 | datum->n * sizeof *datum->values); |
2035 | 0 | ovsdb_atom_clone(&datum->values[idx], value, type->value.type); |
2036 | 0 | } |
2037 | 0 | } |
2038 | 0 | } |
2039 | | |
2040 | | void |
2041 | | ovsdb_datum_add_from_index_unsafe(struct ovsdb_datum *dst, |
2042 | | const struct ovsdb_datum *src, |
2043 | | size_t idx, |
2044 | | const struct ovsdb_type *type) |
2045 | 0 | { |
2046 | 0 | const union ovsdb_atom *key = &src->keys[idx]; |
2047 | 0 | const union ovsdb_atom *value = type->value.type != OVSDB_TYPE_VOID |
2048 | 0 | ? &src->values[idx] |
2049 | 0 | : NULL; |
2050 | 0 | ovsdb_datum_add_unsafe(dst, key, value, type, NULL); |
2051 | 0 | } |
2052 | | |
2053 | | /* Adds 'n' atoms starting from index 'start_idx' from 'src' to the end of |
2054 | | * 'dst'. 'dst' should have enough memory allocated to hold the additional |
2055 | | * 'n' atoms. Atoms are not cloned, i.e. 'dst' will reference the same data. |
2056 | | * Caller also should take care of the result being sorted. */ |
2057 | | static void |
2058 | | ovsdb_datum_push_unsafe(struct ovsdb_datum *dst, |
2059 | | const struct ovsdb_datum *src, |
2060 | | unsigned int start_idx, unsigned int n, |
2061 | | const struct ovsdb_type *type) |
2062 | 0 | { |
2063 | 0 | if (n == 0) { |
2064 | 0 | return; |
2065 | 0 | } |
2066 | | |
2067 | 0 | ovsdb_datum_unshare(dst, type); |
2068 | |
|
2069 | 0 | memcpy(&dst->keys[dst->n], &src->keys[start_idx], n * sizeof src->keys[0]); |
2070 | 0 | if (type->value.type != OVSDB_TYPE_VOID) { |
2071 | 0 | memcpy(&dst->values[dst->n], &src->values[start_idx], |
2072 | 0 | n * sizeof src->values[0]); |
2073 | 0 | } |
2074 | 0 | dst->n += n; |
2075 | 0 | } |
2076 | | |
2077 | | void |
2078 | | ovsdb_datum_union(struct ovsdb_datum *a, const struct ovsdb_datum *b, |
2079 | | const struct ovsdb_type *type) |
2080 | 0 | { |
2081 | 0 | struct ovsdb_datum result; |
2082 | 0 | unsigned int copied, pos; |
2083 | |
|
2084 | 0 | ovsdb_datum_unshare(a, type); |
2085 | 0 | ovsdb_datum_init_empty(&result); |
2086 | |
|
2087 | 0 | copied = 0; |
2088 | 0 | for (size_t bi = 0; bi < b->n; bi++) { |
2089 | 0 | if (ovsdb_datum_find_key(a, &b->keys[bi], type->key.type, &pos)) { |
2090 | | /* Atom with the same key already exists. */ |
2091 | 0 | continue; |
2092 | 0 | } |
2093 | 0 | if (!result.keys) { |
2094 | 0 | ovsdb_datum_reallocate(&result, type, a->n + (b->n - bi)); |
2095 | 0 | } |
2096 | 0 | if (pos > copied) { |
2097 | | /* Need to copy some atoms from 'a' first. */ |
2098 | 0 | ovsdb_datum_push_unsafe(&result, a, copied, pos - copied, type); |
2099 | 0 | copied = pos; |
2100 | 0 | } |
2101 | | /* Inserting new atom from 'b'. */ |
2102 | 0 | ovsdb_atom_clone(&result.keys[result.n], &b->keys[bi], type->key.type); |
2103 | 0 | if (type->value.type != OVSDB_TYPE_VOID) { |
2104 | 0 | ovsdb_atom_clone(&result.values[result.n], &b->values[bi], |
2105 | 0 | type->value.type); |
2106 | 0 | } |
2107 | 0 | result.n++; |
2108 | 0 | } |
2109 | 0 | if (!result.keys) { |
2110 | | /* 'a' doesn't need to be changed. */ |
2111 | 0 | return; |
2112 | 0 | } |
2113 | 0 | if (a->n > copied) { |
2114 | | /* Copying remaining atoms. */ |
2115 | 0 | ovsdb_datum_push_unsafe(&result, a, copied, a->n - copied, type); |
2116 | 0 | } |
2117 | | /* All atoms are copied now. */ |
2118 | 0 | a->n = 0; |
2119 | |
|
2120 | 0 | ovsdb_datum_swap(&result, a); |
2121 | 0 | ovsdb_datum_destroy(&result, type); |
2122 | 0 | } |
2123 | | |
2124 | | void |
2125 | | ovsdb_datum_subtract(struct ovsdb_datum *a, const struct ovsdb_type *a_type, |
2126 | | const struct ovsdb_datum *b, |
2127 | | const struct ovsdb_type *b_type) |
2128 | 0 | { |
2129 | 0 | unsigned int *idx, ai; |
2130 | 0 | size_t n_idx; |
2131 | |
|
2132 | 0 | ovs_assert(a_type->key.type == b_type->key.type); |
2133 | 0 | ovs_assert(a_type->value.type == b_type->value.type |
2134 | 0 | || b_type->value.type == OVSDB_TYPE_VOID); |
2135 | |
|
2136 | 0 | ovsdb_datum_unshare(a, a_type); |
2137 | |
|
2138 | 0 | idx = xmalloc(b->n * sizeof *idx); |
2139 | 0 | n_idx = 0; |
2140 | 0 | for (size_t bi = 0; bi < b->n; bi++) { |
2141 | 0 | ai = ovsdb_datum_find(b, bi, a, b_type); |
2142 | 0 | if (ai == UINT_MAX) { |
2143 | | /* No such atom in 'a'. */ |
2144 | 0 | continue; |
2145 | 0 | } |
2146 | | /* Not destroying right away since ovsdb_datum_find() will use them. */ |
2147 | 0 | idx[n_idx++] = ai; |
2148 | 0 | } |
2149 | 0 | if (!n_idx) { |
2150 | 0 | free(idx); |
2151 | 0 | return; |
2152 | 0 | } |
2153 | | |
2154 | 0 | struct ovsdb_datum result; |
2155 | |
|
2156 | 0 | ovsdb_datum_init_empty(&result); |
2157 | 0 | ovsdb_datum_reallocate(&result, a_type, a->n - n_idx); |
2158 | |
|
2159 | 0 | unsigned int start_idx = 0; |
2160 | 0 | for (size_t i = 0; i < n_idx; i++) { |
2161 | 0 | ai = idx[i]; |
2162 | | |
2163 | | /* Destroying atom. */ |
2164 | 0 | ovsdb_atom_destroy(&a->keys[ai], a_type->key.type); |
2165 | 0 | if (a_type->value.type != OVSDB_TYPE_VOID) { |
2166 | 0 | ovsdb_atom_destroy(&a->values[ai], a_type->value.type); |
2167 | 0 | } |
2168 | | |
2169 | | /* Copy non-removed atoms from 'a' to result. */ |
2170 | 0 | ovsdb_datum_push_unsafe(&result, a, start_idx, ai - start_idx, a_type); |
2171 | 0 | start_idx = idx[i] + 1; |
2172 | 0 | } |
2173 | | /* Copying remaining atoms. */ |
2174 | 0 | ovsdb_datum_push_unsafe(&result, a, start_idx, a->n - start_idx, a_type); |
2175 | 0 | a->n = 0; |
2176 | |
|
2177 | 0 | ovsdb_datum_swap(&result, a); |
2178 | 0 | ovsdb_datum_destroy(&result, a_type); |
2179 | 0 | free(idx); |
2180 | 0 | } |
2181 | | |
2182 | | struct ovsdb_symbol_table * |
2183 | | ovsdb_symbol_table_create(void) |
2184 | 0 | { |
2185 | 0 | struct ovsdb_symbol_table *symtab = xmalloc(sizeof *symtab); |
2186 | 0 | shash_init(&symtab->sh); |
2187 | 0 | return symtab; |
2188 | 0 | } |
2189 | | |
2190 | | void |
2191 | | ovsdb_symbol_table_destroy(struct ovsdb_symbol_table *symtab) |
2192 | 0 | { |
2193 | 0 | if (symtab) { |
2194 | 0 | shash_destroy_free_data(&symtab->sh); |
2195 | 0 | free(symtab); |
2196 | 0 | } |
2197 | 0 | } |
2198 | | |
2199 | | struct ovsdb_symbol * |
2200 | | ovsdb_symbol_table_get(const struct ovsdb_symbol_table *symtab, |
2201 | | const char *name) |
2202 | 0 | { |
2203 | 0 | return shash_find_data(&symtab->sh, name); |
2204 | 0 | } |
2205 | | |
2206 | | struct ovsdb_symbol * |
2207 | | ovsdb_symbol_table_put(struct ovsdb_symbol_table *symtab, const char *name, |
2208 | | const struct uuid *uuid, bool created) |
2209 | 0 | { |
2210 | 0 | struct ovsdb_symbol *symbol; |
2211 | |
|
2212 | 0 | ovs_assert(!ovsdb_symbol_table_get(symtab, name)); |
2213 | 0 | symbol = xmalloc(sizeof *symbol); |
2214 | 0 | symbol->uuid = *uuid; |
2215 | 0 | symbol->created = created; |
2216 | 0 | symbol->strong_ref = false; |
2217 | 0 | symbol->weak_ref = false; |
2218 | 0 | shash_add(&symtab->sh, name, symbol); |
2219 | 0 | return symbol; |
2220 | 0 | } |
2221 | | |
2222 | | struct ovsdb_symbol * |
2223 | | ovsdb_symbol_table_insert(struct ovsdb_symbol_table *symtab, |
2224 | | const char *name) |
2225 | 0 | { |
2226 | 0 | struct ovsdb_symbol *symbol; |
2227 | |
|
2228 | 0 | symbol = ovsdb_symbol_table_get(symtab, name); |
2229 | 0 | if (!symbol) { |
2230 | 0 | struct uuid uuid; |
2231 | |
|
2232 | 0 | uuid_generate(&uuid); |
2233 | 0 | symbol = ovsdb_symbol_table_put(symtab, name, &uuid, false); |
2234 | 0 | } |
2235 | 0 | return symbol; |
2236 | 0 | } |
2237 | | |
2238 | | /* APIs for Generating and apply diffs. */ |
2239 | | |
2240 | | /* Find what needs to be added to and removed from 'old' to construct 'new'. |
2241 | | * |
2242 | | * The 'added' and 'removed' datums are always safe; the orders of keys are |
2243 | | * maintained since they are added in order. */ |
2244 | | void |
2245 | | ovsdb_datum_added_removed(struct ovsdb_datum *added, |
2246 | | struct ovsdb_datum *removed, |
2247 | | const struct ovsdb_datum *old, |
2248 | | const struct ovsdb_datum *new, |
2249 | | const struct ovsdb_type *type) |
2250 | 0 | { |
2251 | 0 | size_t oi, ni; |
2252 | |
|
2253 | 0 | ovsdb_datum_init_empty(added); |
2254 | 0 | ovsdb_datum_init_empty(removed); |
2255 | 0 | if (!ovsdb_type_is_composite(type)) { |
2256 | 0 | ovsdb_datum_clone(removed, old); |
2257 | 0 | ovsdb_datum_clone(added, new); |
2258 | 0 | return; |
2259 | 0 | } |
2260 | | |
2261 | | /* Generate the diff in O(n) time. */ |
2262 | 0 | for (oi = ni = 0; oi < old->n && ni < new->n;) { |
2263 | 0 | int c = ovsdb_atom_compare_3way(&old->keys[oi], &new->keys[ni], |
2264 | 0 | type->key.type); |
2265 | 0 | if (c < 0) { |
2266 | 0 | ovsdb_datum_add_from_index_unsafe(removed, old, oi, type); |
2267 | 0 | oi++; |
2268 | 0 | } else if (c > 0) { |
2269 | 0 | ovsdb_datum_add_from_index_unsafe(added, new, ni, type); |
2270 | 0 | ni++; |
2271 | 0 | } else { |
2272 | 0 | if (type->value.type != OVSDB_TYPE_VOID && |
2273 | 0 | ovsdb_atom_compare_3way(&old->values[oi], &new->values[ni], |
2274 | 0 | type->value.type)) { |
2275 | 0 | ovsdb_datum_add_unsafe(removed, &old->keys[oi], |
2276 | 0 | &old->values[oi], type, NULL); |
2277 | 0 | ovsdb_datum_add_unsafe(added, &new->keys[ni], &new->values[ni], |
2278 | 0 | type, NULL); |
2279 | 0 | } |
2280 | 0 | oi++; ni++; |
2281 | 0 | } |
2282 | 0 | } |
2283 | |
|
2284 | 0 | for (; oi < old->n; oi++) { |
2285 | 0 | ovsdb_datum_add_from_index_unsafe(removed, old, oi, type); |
2286 | 0 | } |
2287 | |
|
2288 | 0 | for (; ni < new->n; ni++) { |
2289 | 0 | ovsdb_datum_add_from_index_unsafe(added, new, ni, type); |
2290 | 0 | } |
2291 | 0 | } |
2292 | | |
2293 | | |
2294 | | /* Generate a difference ovsdb_dataum between 'old' and 'new'. |
2295 | | * 'new' can be regenerated by applying the difference to the 'old'. |
2296 | | * |
2297 | | * The diff operation is reversible. Given 'old', |
2298 | | * 'new' can be recreated by applying diff to 'old'. |
2299 | | * |
2300 | | * Thus |
2301 | | * Let d = 'old' diff 'new' |
2302 | | * then 'new' = 'old' diff d |
2303 | | * |
2304 | | * The 'diff' datum is always safe; the orders of keys are maintained |
2305 | | * since they are added in order. */ |
2306 | | void |
2307 | | ovsdb_datum_diff(struct ovsdb_datum *diff, |
2308 | | const struct ovsdb_datum *old, |
2309 | | const struct ovsdb_datum *new, |
2310 | | const struct ovsdb_type *type) |
2311 | 0 | { |
2312 | 0 | size_t oi, ni; |
2313 | |
|
2314 | 0 | ovsdb_datum_init_empty(diff); |
2315 | 0 | if (!ovsdb_type_is_composite(type)) { |
2316 | 0 | ovsdb_datum_clone(diff, new); |
2317 | 0 | return; |
2318 | 0 | } |
2319 | | |
2320 | | /* Generate the diff in O(n) time. */ |
2321 | 0 | for (oi = ni = 0; oi < old->n && ni < new->n; ) { |
2322 | 0 | int c = ovsdb_atom_compare_3way(&old->keys[oi], &new->keys[ni], |
2323 | 0 | type->key.type); |
2324 | 0 | if (c < 0) { |
2325 | 0 | ovsdb_datum_add_from_index_unsafe(diff, old, oi, type); |
2326 | 0 | oi++; |
2327 | 0 | } else if (c > 0) { |
2328 | 0 | ovsdb_datum_add_from_index_unsafe(diff, new, ni, type); |
2329 | 0 | ni++; |
2330 | 0 | } else { |
2331 | 0 | if (type->value.type != OVSDB_TYPE_VOID && |
2332 | 0 | ovsdb_atom_compare_3way(&old->values[oi], &new->values[ni], |
2333 | 0 | type->value.type)) { |
2334 | 0 | ovsdb_datum_add_unsafe(diff, &new->keys[ni], &new->values[ni], |
2335 | 0 | type, NULL); |
2336 | 0 | } |
2337 | 0 | oi++; ni++; |
2338 | 0 | } |
2339 | 0 | } |
2340 | |
|
2341 | 0 | for (; oi < old->n; oi++) { |
2342 | 0 | ovsdb_datum_add_from_index_unsafe(diff, old, oi, type); |
2343 | 0 | } |
2344 | |
|
2345 | 0 | for (; ni < new->n; ni++) { |
2346 | 0 | ovsdb_datum_add_from_index_unsafe(diff, new, ni, type); |
2347 | 0 | } |
2348 | 0 | } |
2349 | | |
2350 | | /* Apply 'diff' to 'a'. |
2351 | | * |
2352 | | * Return NULL if the 'a' is successfully updated, otherwise, return |
2353 | | * ovsdb_error. */ |
2354 | | struct ovsdb_error * |
2355 | | ovsdb_datum_apply_diff_in_place(struct ovsdb_datum *a, |
2356 | | const struct ovsdb_datum *diff, |
2357 | | const struct ovsdb_type *type) |
2358 | 0 | { |
2359 | 0 | struct ovsdb_error *error = NULL; |
2360 | 0 | struct ovsdb_datum result; |
2361 | 0 | size_t i, new_size; |
2362 | 0 | unsigned int *idx, pos; |
2363 | 0 | enum { |
2364 | 0 | DIFF_OP_ADD, |
2365 | 0 | DIFF_OP_REMOVE, |
2366 | 0 | DIFF_OP_UPDATE, |
2367 | 0 | } *operation; |
2368 | |
|
2369 | 0 | if (!ovsdb_type_is_composite(type)) { |
2370 | 0 | ovsdb_datum_destroy(a, type); |
2371 | 0 | ovsdb_datum_clone(a, diff); |
2372 | 0 | return NULL; |
2373 | 0 | } |
2374 | | |
2375 | 0 | ovsdb_datum_unshare(a, type); |
2376 | |
|
2377 | 0 | operation = xmalloc(diff->n * sizeof *operation); |
2378 | 0 | idx = xmalloc(diff->n * sizeof *idx); |
2379 | 0 | new_size = a->n; |
2380 | 0 | for (i = 0; i < diff->n; i++) { |
2381 | 0 | if (!ovsdb_datum_find_key(a, &diff->keys[i], type->key.type, &pos)) { |
2382 | 0 | operation[i] = DIFF_OP_ADD; |
2383 | 0 | new_size++; |
2384 | 0 | } else if (type->value.type != OVSDB_TYPE_VOID |
2385 | 0 | && !ovsdb_atom_equals(&diff->values[i], &a->values[pos], |
2386 | 0 | type->value.type)) { |
2387 | 0 | operation[i] = DIFF_OP_UPDATE; |
2388 | 0 | } else { |
2389 | 0 | operation[i] = DIFF_OP_REMOVE; |
2390 | 0 | new_size--; |
2391 | 0 | } |
2392 | 0 | idx[i] = pos; |
2393 | 0 | } |
2394 | | |
2395 | | /* Make sure member size of 'new' conforms to type. */ |
2396 | 0 | if (new_size < type->n_min || new_size > type->n_max) { |
2397 | 0 | error = ovsdb_error(NULL, "Datum crated by diff has size error"); |
2398 | 0 | goto exit; |
2399 | 0 | } |
2400 | | |
2401 | 0 | ovsdb_datum_init_empty(&result); |
2402 | 0 | ovsdb_datum_reallocate(&result, type, new_size); |
2403 | |
|
2404 | 0 | unsigned int copied = 0; |
2405 | 0 | for (i = 0; i < diff->n; i++) { |
2406 | 0 | pos = idx[i]; |
2407 | |
|
2408 | 0 | if (copied < pos) { |
2409 | | /* Copying all atoms that should go before the current one. */ |
2410 | 0 | ovsdb_datum_push_unsafe(&result, a, copied, pos - copied, type); |
2411 | 0 | copied = pos; |
2412 | 0 | } |
2413 | |
|
2414 | 0 | switch (operation[i]) { |
2415 | 0 | case DIFF_OP_UPDATE: |
2416 | 0 | case DIFF_OP_ADD: |
2417 | | /* Inserting new atom from 'diff'. */ |
2418 | 0 | ovsdb_atom_clone(&result.keys[result.n], |
2419 | 0 | &diff->keys[i], type->key.type); |
2420 | 0 | if (type->value.type != OVSDB_TYPE_VOID) { |
2421 | 0 | ovsdb_atom_clone(&result.values[result.n], |
2422 | 0 | &diff->values[i], type->value.type); |
2423 | 0 | } |
2424 | 0 | result.n++; |
2425 | 0 | if (operation[i] != DIFF_OP_UPDATE) { |
2426 | 0 | break; |
2427 | 0 | } |
2428 | | /* fall through */ |
2429 | | |
2430 | 0 | case DIFF_OP_REMOVE: |
2431 | | /* Destroying atom. */ |
2432 | 0 | ovsdb_atom_destroy(&a->keys[pos], type->key.type); |
2433 | 0 | if (type->value.type != OVSDB_TYPE_VOID) { |
2434 | 0 | ovsdb_atom_destroy(&a->values[pos], type->value.type); |
2435 | 0 | } |
2436 | 0 | copied++; /* Skipping removed atom. */ |
2437 | 0 | break; |
2438 | 0 | } |
2439 | 0 | } |
2440 | | /* Copying remaining atoms. */ |
2441 | 0 | ovsdb_datum_push_unsafe(&result, a, copied, a->n - copied, type); |
2442 | 0 | a->n = 0; |
2443 | |
|
2444 | 0 | ovsdb_datum_swap(&result, a); |
2445 | 0 | ovsdb_datum_destroy(&result, type); |
2446 | 0 | exit: |
2447 | 0 | free(operation); |
2448 | 0 | free(idx); |
2449 | 0 | return error; |
2450 | 0 | } |
2451 | | |
2452 | | /* Apply 'diff' to 'old' to regenerate 'new'. |
2453 | | * |
2454 | | * Return NULL if the 'new' is successfully generated, otherwise, return |
2455 | | * ovsdb_error and the stat of 'new' is indeterministic. */ |
2456 | | struct ovsdb_error * |
2457 | | ovsdb_datum_apply_diff(struct ovsdb_datum *new, |
2458 | | const struct ovsdb_datum *old, |
2459 | | const struct ovsdb_datum *diff, |
2460 | | const struct ovsdb_type *type) |
2461 | 0 | { |
2462 | 0 | ovsdb_datum_diff(new, old, diff, type); |
2463 | | |
2464 | | /* Make sure member size of 'new' conforms to type. */ |
2465 | 0 | if (new->n < type->n_min || new->n > type->n_max) { |
2466 | 0 | ovsdb_datum_destroy(new, type); |
2467 | 0 | return ovsdb_error(NULL, "Datum crated by diff has size error"); |
2468 | 0 | } |
2469 | | |
2470 | 0 | return NULL; |
2471 | 0 | } |
2472 | | |
2473 | | |
2474 | | /* Extracts a token from the beginning of 's' and returns a pointer just after |
2475 | | * the token. Stores the token itself into '*outp', which the caller is |
2476 | | * responsible for freeing (with free()). |
2477 | | * |
2478 | | * If 's[0]' is a delimiter, the returned token is the empty string. |
2479 | | * |
2480 | | * A token extends from 's' to the first delimiter, as defined by |
2481 | | * ovsdb_token_is_delim(), or until the end of the string. A delimiter can be |
2482 | | * escaped with a backslash, in which case the backslash does not appear in the |
2483 | | * output. Double quotes also cause delimiters to be ignored, but the double |
2484 | | * quotes are retained in the output. (Backslashes inside double quotes are |
2485 | | * not removed, either.) |
2486 | | */ |
2487 | | char * |
2488 | | ovsdb_token_parse(const char **s, char **outp) |
2489 | 0 | { |
2490 | 0 | const char *p; |
2491 | 0 | struct ds out; |
2492 | 0 | bool in_quotes; |
2493 | 0 | char *error; |
2494 | |
|
2495 | 0 | ds_init(&out); |
2496 | 0 | in_quotes = false; |
2497 | 0 | for (p = *s; *p != '\0'; ) { |
2498 | 0 | int c = *p++; |
2499 | 0 | if (c == '\\') { |
2500 | 0 | if (in_quotes) { |
2501 | 0 | ds_put_char(&out, '\\'); |
2502 | 0 | } |
2503 | 0 | if (!*p) { |
2504 | 0 | error = xasprintf("%s: backslash at end of argument", *s); |
2505 | 0 | goto error; |
2506 | 0 | } |
2507 | 0 | ds_put_char(&out, *p++); |
2508 | 0 | } else if (!in_quotes && ovsdb_token_is_delim(c)) { |
2509 | 0 | p--; |
2510 | 0 | break; |
2511 | 0 | } else { |
2512 | 0 | ds_put_char(&out, c); |
2513 | 0 | if (c == '"') { |
2514 | 0 | in_quotes = !in_quotes; |
2515 | 0 | } |
2516 | 0 | } |
2517 | 0 | } |
2518 | 0 | if (in_quotes) { |
2519 | 0 | error = xasprintf("%s: quoted string extends past end of argument", |
2520 | 0 | *s); |
2521 | 0 | goto error; |
2522 | 0 | } |
2523 | 0 | *outp = ds_cstr(&out); |
2524 | 0 | *s = p; |
2525 | 0 | return NULL; |
2526 | | |
2527 | 0 | error: |
2528 | 0 | ds_destroy(&out); |
2529 | 0 | *outp = NULL; |
2530 | 0 | return error; |
2531 | 0 | } |
2532 | | |
2533 | | /* Returns true if 'c' delimits tokens, or if 'c' is 0, and false otherwise. */ |
2534 | | bool |
2535 | | ovsdb_token_is_delim(unsigned char c) |
2536 | 0 | { |
2537 | 0 | return strchr(":=, []{}!<>", c) != NULL; |
2538 | 0 | } |
2539 | | |
2540 | | struct ovsdb_error * |
2541 | | ovsdb_atom_range_check_size(int64_t range_start, int64_t range_end) |
2542 | 0 | { |
2543 | 0 | if ((uint64_t) range_end - (uint64_t) range_start |
2544 | 0 | >= MAX_OVSDB_ATOM_RANGE_SIZE) { |
2545 | 0 | return ovsdb_error("constraint violation", |
2546 | 0 | "Range \"%"PRId64"-%"PRId64"\" is too big. " |
2547 | 0 | "Maximum allowed size is %d.", |
2548 | 0 | range_start, range_end, MAX_OVSDB_ATOM_RANGE_SIZE); |
2549 | 0 | } |
2550 | 0 | return NULL; |
2551 | 0 | } |
2552 | | |
2553 | | char * |
2554 | | ovsdb_data_row_name(const struct uuid *uuid) |
2555 | 0 | { |
2556 | 0 | char *name; |
2557 | 0 | char *p; |
2558 | |
|
2559 | 0 | name = xasprintf("row"UUID_FMT, UUID_ARGS(uuid)); |
2560 | 0 | for (p = name; *p != '\0'; p++) { |
2561 | 0 | if (*p == '-') { |
2562 | 0 | *p = '_'; |
2563 | 0 | } |
2564 | 0 | } |
2565 | |
|
2566 | 0 | return name; |
2567 | 0 | } |