1 /*
2 Copyright (c) 1994 - 2010, Lawrence Livermore National Security, LLC.
3 LLNL-CODE-425250.
4 All rights reserved.
5
6 This file is part of Silo. For details, see silo.llnl.gov.
7
8 Redistribution and use in source and binary forms, with or without
9 modification, are permitted provided that the following conditions
10 are met:
11
12 * Redistributions of source code must retain the above copyright
13 notice, this list of conditions and the disclaimer below.
14 * Redistributions in binary form must reproduce the above copyright
15 notice, this list of conditions and the disclaimer (as noted
16 below) in the documentation and/or other materials provided with
17 the distribution.
18 * Neither the name of the LLNS/LLNL nor the names of its
19 contributors may be used to endorse or promote products derived
20 from this software without specific prior written permission.
21
22 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
25 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL LAWRENCE
26 LIVERMORE NATIONAL SECURITY, LLC, THE U.S. DEPARTMENT OF ENERGY OR
27 CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
28 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
29 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34
35 This work was produced at Lawrence Livermore National Laboratory under
36 Contract No. DE-AC52-07NA27344 with the DOE.
37
38 Neither the United States Government nor Lawrence Livermore National
39 Security, LLC nor any of their employees, makes any warranty, express
40 or implied, or assumes any liability or responsibility for the
41 accuracy, completeness, or usefulness of any information, apparatus,
42 product, or process disclosed, or represents that its use would not
43 infringe privately-owned rights.
44
45 Any reference herein to any specific commercial products, process, or
46 services by trade name, trademark, manufacturer or otherwise does not
47 necessarily constitute or imply its endorsement, recommendation, or
48 favoring by the United States Government or Lawrence Livermore
49 National Security, LLC. The views and opinions of authors expressed
50 herein do not necessarily state or reflect those of the United States
51 Government or Lawrence Livermore National Security, LLC, and shall not
52 be used for advertising or product endorsement purposes.
53 */
54 #include <errno.h>
55 #include <fcntl.h>
56 #include <float.h>
57 #include <limits.h>
58 #include <sys/types.h>
59 #include <sys/stat.h>
60 #include <unistd.h>
61
62 #include <silo_private.h>
63
64 #include <silo_json.h>
65 #include <json/json.h>
66 #include <json/json_object_private.h>
67 #include <json/printbuf.h>
68
69 #define EXTPTR_HDRSTR "\":{\"ptr\":\"0x"
70
indent(struct printbuf * pb,int level,int flags)71 static void indent(struct printbuf *pb, int level, int flags)
72 {
73 if (flags & JSON_C_TO_STRING_PRETTY)
74 {
75 printbuf_memset(pb, -1, ' ', level * 2);
76 }
77 }
78
json_object_object_length(struct json_object * o)79 static int json_object_object_length(struct json_object *o)
80 {
81 struct lh_table *t = json_object_get_object(o);
82 return t->count;
83 }
84
json_object_object_get_member_count(struct json_object * o)85 static int json_object_object_get_member_count(struct json_object *o)
86 {
87 int n = 0;
88 struct json_object_iter jiter;
89 json_object_object_foreachC(o, jiter)
90 {
91 n++;
92 }
93 return n;
94 }
95
json_object_set_serializer(json_object * jso,json_object_to_json_string_fn to_string_func,void * userdata,json_object_delete_fn * user_delete)96 static void json_object_set_serializer(json_object *jso,
97 json_object_to_json_string_fn to_string_func,
98 void * userdata, json_object_delete_fn * user_delete)
99 {
100 jso->_to_json_string = to_string_func;
101 }
102
json_object_set_deleter(json_object * jso,json_object_delete_fn delete_func)103 static void json_object_set_deleter(json_object *jso,
104 json_object_delete_fn delete_func)
105 {
106 jso->_delete = delete_func;
107 }
108
109 void
json_object_extptr_delete(struct json_object * jso)110 json_object_extptr_delete(struct json_object *jso)
111 {
112 void *extptr = json_object_get_extptr_ptr(jso);
113 if (extptr) free(extptr);
114 json_object_put(jso);
115 }
116
117 static int
json_object_extptr_to_json_string(struct json_object * jso,struct printbuf * pb,int level,int flags)118 json_object_extptr_to_json_string(struct json_object* jso,
119 struct printbuf *pb, int level, int flags)
120 {
121 int had_children = 0;
122 int ii;
123 char *p;
124 int datatype, nvals, ndims, dims[32];
125 struct json_object *darr, *subobj;
126 int retval;
127
128 sprintbuf(pb, "{\"ptr\":%s", json_object_to_json_string(json_object_object_get(jso, "ptr")));
129 sprintbuf(pb, ",\"datatype\":%s", json_object_to_json_string(json_object_object_get(jso, "datatype")));
130 sprintbuf(pb, ",\"ndims\":%s", json_object_to_json_string(json_object_object_get(jso, "ndims")));
131 sprintbuf(pb, ",\"dims\":%s", json_object_to_json_string(json_object_object_get(jso, "dims")));
132
133 p = (char*) json_object_get_strptr(json_object_object_get(jso, "ptr"));
134 datatype = json_object_get_int(json_object_object_get(jso, "datatype"));
135 ndims = json_object_get_int(json_object_object_get(jso, "ndims"));
136 darr = json_object_object_get(jso, "dims");
137 nvals = 1;
138 for (ii = 0; ii < ndims; ii++)
139 {
140 dims[ii] = json_object_get_int(json_object_array_get_idx(darr, ii));
141 nvals *= dims[ii];
142 }
143
144 if (flags & JSON_C_TO_STRING_EXTPTR_AS_BINARY)
145 {
146 sprintbuf(pb, ",\"nvals\":%d", nvals);
147 sprintbuf(pb, ",\"data\":[");
148 printbuf_memappend(pb, p, nvals*db_GetMachDataSize(datatype));
149 }
150 else if (flags & JSON_C_TO_STRING_EXTPTR_SKIP)
151 {
152 return sprintbuf(pb, "}");
153 }
154 else
155 {
156
157 sprintbuf(pb, ",\"data\":[");
158 if (flags & JSON_C_TO_STRING_PRETTY)
159 sprintbuf(pb, "\n");
160
161 for(ii=0; ii < nvals; ii++)
162 {
163 struct json_object *val;
164 if (had_children)
165 {
166 sprintbuf(pb, ",");
167 if (flags & JSON_C_TO_STRING_PRETTY)
168 sprintbuf(pb, "\n");
169 }
170 had_children = 1;
171 if (flags & JSON_C_TO_STRING_SPACED)
172 sprintbuf(pb, " ");
173 indent(pb, level + 1, flags);
174
175 switch (datatype)
176 {
177 case DB_CHAR:
178 {
179 sprintbuf(pb, "%c", *((char*)p));
180 p += sizeof(char);
181 break;
182 }
183 case DB_SHORT:
184 {
185 sprintbuf(pb, "%hd", *((short*)p));
186 p += sizeof(short);
187 break;
188 }
189 case DB_INT:
190 {
191 sprintbuf(pb, "%d", *((int*)p));
192 p += sizeof(int);
193 break;
194 }
195 case DB_LONG:
196 {
197 sprintbuf(pb, "%ld", *((long*)p));
198 p += sizeof(long);
199 break;
200 }
201 case DB_LONG_LONG:
202 {
203 sprintbuf(pb, "%lld", *((long long*)p));
204 p += sizeof(long long);
205 break;
206 }
207 case DB_FLOAT:
208 {
209 sprintbuf(pb, "%f", *((float*)p));
210 p += sizeof(float);
211 break;
212 }
213 case DB_DOUBLE:
214 {
215 sprintbuf(pb, "%f", *((double*)p));
216 p += sizeof(double);
217 break;
218 }
219 }
220 }
221 }
222 if (flags & JSON_C_TO_STRING_PRETTY)
223 {
224 if (had_children)
225 sprintbuf(pb, "\n");
226 indent(pb,level,flags);
227 }
228
229 if (flags & JSON_C_TO_STRING_SPACED)
230 retval = sprintbuf(pb, " ]}");
231 else
232 retval = sprintbuf(pb, "]}");
233
234 return retval;
235 }
236
237 struct json_object *
json_object_new_strptr(void * p)238 json_object_new_strptr(void *p)
239 {
240 static char tmp[32];
241 if (sizeof(p) == sizeof(unsigned))
242 snprintf(tmp, sizeof(tmp), "0x%016x", (unsigned) p);
243 else if (sizeof(p) == sizeof(unsigned long))
244 snprintf(tmp, sizeof(tmp), "0x%016lx", (unsigned long) p);
245 else if (sizeof(p) == sizeof(unsigned long long))
246 snprintf(tmp, sizeof(tmp), "0x%016llx", (unsigned long long) p);
247
248 return json_object_new_string(tmp);
249 }
250
251 void *
json_object_get_strptr(struct json_object * o)252 json_object_get_strptr(struct json_object *o)
253 {
254 void *p;
255 char const *strptr = json_object_get_string(o);
256 if (sscanf(strptr, "%p", &p) == 1)
257 return p;
258 else
259 return 0;
260 }
261
262 struct json_object *
json_object_new_extptr(void * p,int ndims,int const * dims,int datatype)263 json_object_new_extptr(void *p, int ndims, int const *dims, int datatype)
264 {
265 int i;
266 struct json_object *jobj = json_object_new_object();
267 struct json_object *jarr = json_object_new_array();
268
269 json_object_set_serializer(jobj, json_object_extptr_to_json_string, 0, 0);
270 json_object_set_deleter(jobj, json_object_extptr_delete);
271
272 for (i = 0; i < ndims; i++)
273 json_object_array_add(jarr, json_object_new_int(dims[i]));
274
275 json_object_object_add(jobj, "ptr", json_object_new_strptr(p));
276 json_object_object_add(jobj, "datatype", json_object_new_int(datatype));
277 json_object_object_add(jobj, "ndims", json_object_new_int(ndims));
278 json_object_object_add(jobj, "dims", jarr);
279
280 return jobj;
281 }
282
283 int
json_object_is_extptr(struct json_object * obj)284 json_object_is_extptr(struct json_object *obj)
285 {
286 if (json_object_object_get_ex(obj, "ptr", 0) &&
287 json_object_object_get_ex(obj, "datatype", 0) &&
288 json_object_object_get_ex(obj, "ndims", 0) &&
289 json_object_object_get_ex(obj, "dims", 0))
290 return 1;
291 return 0;
292 }
293
294 int
json_object_get_extptr_ndims(struct json_object * obj)295 json_object_get_extptr_ndims(struct json_object *obj)
296 {
297 struct json_object *sobj = 0;
298 if (json_object_object_get_ex(obj, "ptr", 0) &&
299 json_object_object_get_ex(obj, "datatype", 0) &&
300 json_object_object_get_ex(obj, "ndims", &sobj) &&
301 json_object_object_get_ex(obj, "dims", 0) && sobj)
302 return json_object_get_int(sobj);
303 return -1;
304 }
305
306 int
json_object_get_extptr_datatype(struct json_object * obj)307 json_object_get_extptr_datatype(struct json_object *obj)
308 {
309 struct json_object *sobj = 0;
310 if (json_object_object_get_ex(obj, "ptr", 0) &&
311 json_object_object_get_ex(obj, "datatype", &sobj) &&
312 json_object_object_get_ex(obj, "ndims", 0) &&
313 json_object_object_get_ex(obj, "dims", 0) && sobj)
314 return json_object_get_int(sobj);
315 return -1;
316 }
317
318 int
json_object_get_extptr_dims_idx(struct json_object * obj,int idx)319 json_object_get_extptr_dims_idx(struct json_object *obj, int idx)
320 {
321 struct json_object *sobj = 0;
322 if (json_object_object_get_ex(obj, "ptr", 0) &&
323 json_object_object_get_ex(obj, "datatype", 0) &&
324 json_object_object_get_ex(obj, "ndims", 0) &&
325 json_object_object_get_ex(obj, "dims", &sobj) && sobj)
326 return json_object_get_int(json_object_array_get_idx(sobj, idx));
327 return 0;
328 }
329
330 void *
json_object_get_extptr_ptr(struct json_object * obj)331 json_object_get_extptr_ptr(struct json_object *obj)
332 {
333 struct json_object *sobj = 0;
334 if (json_object_object_get_ex(obj, "ptr", &sobj) &&
335 json_object_object_get_ex(obj, "datatype", 0) &&
336 json_object_object_get_ex(obj, "ndims", 0) &&
337 json_object_object_get_ex(obj, "dims", 0) && sobj)
338 return json_object_get_strptr(sobj);
339 return 0;
340 }
341
342 int
json_object_to_binary_buf(struct json_object * obj,int flags,void ** buf,int * len)343 json_object_to_binary_buf(struct json_object *obj, int flags, void **buf, int *len)
344 {
345 char const *pjhdr;
346 struct printbuf *pb = printbuf_new();
347
348 /* first, stringify the json object as normal but skipping extptr data */
349 char const *jhdr = json_object_to_json_string_ext(obj, flags|JSON_C_TO_STRING_EXTPTR_SKIP);
350 sprintbuf(pb, "%s", jhdr);
351 sprintbuf(pb, "%c", '\0'); /* so header is null-terminated */
352
353 /* now, handle all extptr objects by appending their binary data onto the end,
354 and over-writing the "ptr" value with offset within the buffer */
355 pjhdr = jhdr;
356 while (*pjhdr)
357 {
358 if (!strncmp(pjhdr, EXTPTR_HDRSTR, sizeof(EXTPTR_HDRSTR)-1))
359 {
360 char tmp[64];
361 int pblen;
362 struct json_object *extptr_obj = json_tokener_parse(pjhdr+2); /* walk past '":' */
363 void *p = json_object_get_strptr(json_object_object_get(extptr_obj, "ptr"));
364 int datatype = json_object_get_int(json_object_object_get(extptr_obj, "datatype"));
365 int ndims = json_object_get_int(json_object_object_get(extptr_obj, "ndims"));
366 struct json_object *darr = json_object_object_get(extptr_obj, "dims");
367 int ii, nvals = 1;
368 for (ii = 0; ii < ndims; ii++)
369 nvals *= json_object_get_int(json_object_array_get_idx(darr, ii));
370 pblen = printbuf_length(pb);
371 printbuf_memappend_fast(pb, p, nvals*db_GetMachDataSize(datatype));
372 /* Use of a hexadecimal value works ok here because a scanf can read it */
373 sprintf(tmp,"%-.16x",pblen); /* overwrite ptr value w/buffer-offset */
374 memcpy(pb->buf + (pjhdr+12-jhdr),tmp,strlen(tmp)); /* overwrite ptr value w/buffer-offset */
375 pjhdr += sizeof(EXTPTR_HDRSTR);
376 json_object_put(extptr_obj);
377 }
378 pjhdr++;
379 }
380 if (len) *len = printbuf_length(pb);
381 if (buf) *buf = pb->buf;
382 free(pb);
383 return 0;
384 }
385
386 static void
json_object_from_binary_buf_recurse(struct json_object * jso,void * buf)387 json_object_from_binary_buf_recurse(struct json_object *jso, void *buf)
388 {
389 /* first, reconstitute the header */
390 struct json_object_iter iter;
391
392 json_object_object_foreachC(jso, iter)
393 {
394 json_type jtype = json_object_get_type(iter.val);
395 if (jtype == json_type_object)
396 {
397 if (json_object_object_get_ex(iter.val, "ptr", 0) &&
398 json_object_object_get_ex(iter.val, "datatype", 0) &&
399 json_object_object_get_ex(iter.val, "ndims", 0) &&
400 json_object_object_get_ex(iter.val, "dims", 0))
401 {
402 char strptr[128];
403 void *p;
404 int i, offset, nvals=1;
405 char const *offstr = json_object_get_string(json_object_object_get(iter.val, "ptr"));
406 int datatype = json_object_get_int(json_object_object_get(iter.val, "datatype"));
407 int ndims = json_object_get_int(json_object_object_get(iter.val, "ndims"));
408 struct json_object *darr = json_object_object_get(iter.val, "dims");
409 for (i = 0; i < ndims; i++)
410 nvals *= json_object_get_int(json_object_array_get_idx(darr, i));
411 sscanf(offstr, "%x", &offset);
412 p = malloc(nvals*db_GetMachDataSize(datatype));
413 memcpy(p, buf+offset, nvals*db_GetMachDataSize(datatype));
414 json_object_object_del(iter.val,"ptr");
415 snprintf(strptr, sizeof(strptr), "%p", p);
416 json_object_object_add(iter.val, "ptr", json_object_new_string(strptr));
417 }
418 else
419 {
420 json_object_from_binary_buf_recurse(iter.val, buf);
421 }
422 }
423 }
424 }
425
426 struct json_object *
json_object_from_binary_buf(void * buf,int len)427 json_object_from_binary_buf(void *buf, int len)
428 {
429 struct json_object *retval = json_tokener_parse((char*)buf);
430 json_object_from_binary_buf_recurse(retval, buf);
431 return retval;
432 }
433
434 struct json_object *
json_object_from_binary_file(char const * filename)435 json_object_from_binary_file(char const *filename)
436 {
437 struct json_object *retval;
438 void *buf;
439 int fd;
440
441 #ifndef SIZEOF_OFF64_T
442 #error missing definition for SIZEOF_OFF64_T in silo_private.h
443 #else
444 struct stat s;
445 #endif
446
447 errno = 0;
448 memset(&s, 0, sizeof(s));
449
450 #if SIZEOF_OFF64_T > 4
451 if (stat64(filename, &s) != 0 || errno != 0)
452 return 0;
453 #else
454 if (stat(filename, &s) != 0 || errno != 0)
455 return 0;
456 #endif
457
458 fd = open(filename, O_RDONLY);
459 if (fd < 0)
460 return 0;
461 buf = malloc(s.st_size);
462 if (read(fd, buf, (size_t) s.st_size) != (ssize_t) s.st_size)
463 {
464 free(buf);
465 return 0;
466 }
467 close(fd);
468 retval = json_object_from_binary_buf(buf, (int) s.st_size);
469 free(buf);
470
471 return retval;
472 }
473
474 int
json_object_to_binary_file(char const * filename,struct json_object * obj)475 json_object_to_binary_file(char const *filename, struct json_object *obj)
476 {
477 void *buf; int len; int fd;
478
479 json_object_to_binary_buf(obj, 0, &buf, &len);
480 fd = open(filename, O_CREAT|O_TRUNC|O_WRONLY, S_IRUSR|S_IWUSR);
481 write(fd, buf, len);
482 close(fd);
483 free(buf);
484 return 0;
485 }
486
487 int
json_object_reconstitute_extptrs(struct json_object * obj)488 json_object_reconstitute_extptrs(struct json_object *obj)
489 {
490 struct json_object_iter jiter;
491 struct json_object *parent_jobjs_with_extptr_members[1000];
492 char const *extptr_member_keys[1000];
493 int k, num_to_remove = 0;
494
495 json_object_object_foreachC(obj, jiter)
496 {
497 struct json_object *ptr_obj, *datatype_obj, *ndims_obj, *dims_obj, *data_obj;
498
499 struct json_object *mobj = jiter.val;
500 char const *mname = jiter.key;
501
502 if (json_object_get_type(mobj) != json_type_object)
503 continue;
504
505 if (!(json_object_object_get_ex(mobj, "ptr", &ptr_obj ) &&
506 json_object_object_get_ex(mobj, "datatype", &datatype_obj) &&
507 json_object_object_get_ex(mobj, "ndims", &ndims_obj) &&
508 json_object_object_get_ex(mobj, "dims", &dims_obj) &&
509 json_object_object_get_ex(mobj, "data", &data_obj)))
510 {
511 json_object_reconstitute_extptrs(mobj);
512 continue;
513 }
514
515 if (!(json_object_get_type(ptr_obj) == json_type_string &&
516 json_object_get_type(datatype_obj) == json_type_int &&
517 json_object_get_type(ndims_obj) == json_type_int &&
518 json_object_get_type(dims_obj) == json_type_array &&
519 json_object_get_type(data_obj) == json_type_array))
520 {
521 json_object_reconstitute_extptrs(mobj);
522 continue;
523 }
524
525 if (json_object_object_get_member_count(mobj) != 5)
526 continue;
527
528 extptr_member_keys[num_to_remove] = mname;
529 parent_jobjs_with_extptr_members[num_to_remove] = obj;
530 num_to_remove++;
531 }
532
533 for (k = 0; k < num_to_remove; k++)
534 {
535 struct json_object *ptr_obj, *datatype_obj, *ndims_obj, *dims_obj, *data_obj;
536 char *mname = strdup(extptr_member_keys[k]);
537 struct json_object *pobj = parent_jobjs_with_extptr_members[k];
538 struct json_object *extptr_obj;
539
540 json_object_object_get_ex(pobj, mname, &extptr_obj);
541 json_object_object_get_ex(extptr_obj, "ptr", &ptr_obj );
542 json_object_object_get_ex(extptr_obj, "datatype", &datatype_obj);
543 json_object_object_get_ex(extptr_obj, "ndims", &ndims_obj);
544 json_object_object_get_ex(extptr_obj, "dims", &dims_obj);
545 json_object_object_get_ex(extptr_obj, "data", &data_obj);
546
547 /* We're at an extptr object that was serialized to a 'standard' json string.
548 * So, lets reconstitute it. */
549 {
550 int i, n = 1;
551 int datatype = json_object_get_int(datatype_obj);
552 int ndims = json_object_get_int(ndims_obj);
553 int *dims = (int *) malloc(ndims * sizeof(int));
554 int jdtype = json_object_get_type(json_object_array_get_idx(data_obj, 0));
555 char *p;
556 void *pdata;
557
558 /* get the array dimension sizes */
559 for (i = 0; i < ndims; i++)
560 {
561 dims[i] = json_object_get_int(json_object_array_get_idx(dims_obj, i));
562 n *= dims[i];
563 }
564
565 /* get the array data */
566 pdata = (void *) malloc(n * db_GetMachDataSize(datatype));
567 for (i = 0, p = pdata; i < n; i++, p += db_GetMachDataSize(datatype))
568 {
569 switch (jdtype)
570 {
571 case json_type_int:
572 {
573 int ival = json_object_get_int(json_object_array_get_idx(data_obj, i));
574 if (datatype == DB_CHAR)
575 *((char*)p) = (unsigned char) ival;
576 else if (datatype == DB_SHORT)
577 *((short*)p) = (short) ival;
578 else if (datatype == DB_INT)
579 *((int*)p) = ival;
580 else if (datatype == DB_LONG)
581 *((long*)p) = (long) ival;
582 break;
583 }
584 case json_type_double:
585 {
586 double dval = json_object_get_double(json_object_array_get_idx(data_obj, i));
587 if (datatype == DB_DOUBLE)
588 *((double*)p) = dval;
589 else
590 *((float*)p) = (float) dval;
591 break;
592 }
593 }
594 }
595
596 /* delete the old object */
597 json_object_object_del(pobj, mname);
598
599 /* Add the reconstituted extptr object */
600 json_object_object_add(pobj, mname,
601 json_object_new_extptr(pdata, ndims, dims, datatype));
602
603 free(mname);
604 }
605 }
606
607 return 0;
608 }
609
610 #if 0
611 /* Difference methods
612 *
613 * Easiest thing to develop is a thing to test equality. Given two objects, they
614 * are equal if all their sub-components are equal. But, that isn't the same as
615 * computing the difference itself.
616 *
617 * Given two json objects, what is the difference object?
618 * If both objects contain a member in common (e.g. same key/idx and value-type), diff that member
619 * If their difference is "zero", return json_type_null
620 * If their difference is non-zero, return a new json_object holding the difference
621 * If there is an object in one but not the other,
622 * Ignore it or copy it to the difference object
623 *
624 * If caller wants just to 'print' the differences, then caller passes a stream
625 * or a printbuf into which the difference details are sprint'd
626 *
627 * If caller simply wants to know if the objects are different or not
628 *
629 * Options
630 * * just return if the objecs are equal or not (e.g. check if diff is zero)
631 * * ignoring exclusive members
632 * * Compute a difference object
633 * * ignoring exclusive members
634 * * compute a 'total' difference or only map
635 * * print the differences
636 * * ignore exclusive members
637 * * compute a total difference
638 *
639 * Consider the following example cases
640 * Diffing an array of integer values (or extptr thingy)
641 * 1. stop on first diff, return false then, otherwise true
642 * 2. create a new array with zeros (json_type_null or special zero object) where diffs<tol and diffs otherwise
643 * 3. create a new array with only the diffs>tol and their indx's
644 * Diffing an object with same name/type members
645 * 1. stop on first diff, return false then, otherwise true
646 * 2. create a new object with zeros (json_type_null or special zero object) where diffs<tol and diffs otherwise
647 * 3. create a new object with only the different members and their names
648 * Diffing a single primitive
649 * 1. return 1 if different otherwise 0
650 * 2. return a null or zero object if diff<tol
651 * 3. create a new primitive object with the difference
652 * Diffing primitives of differing type
653 * Promotion order: boolean->int->double->string
654 * ^ ^
655 * string(int)-->| |
656 * string(real)------>|
657 * Always promote to highest and diff there
658 * Diffing arbitrary objects
659 * What constitutes a diff?
660 * tolerances
661 * ignore exclusive members (structure)
662 * match idx/key but not type
663 * How is diff returned?
664 * as a bool
665 * as an object
666 * modifications (e.g. map) or toleranced diffs (e.g. total), insertions, deletions
667 * as a printbuf
668 * modifications (e.g. map) or toleranced diffs (e.g. total), insertions, deletions
669 *
670 *
671 * String output might look like...
672 * left { foo:5, bar:123.5, gorfo:{a:5, b:0.002567, c:"abc"}, q="mark", p=0}
673 * right { foo:5, bar:123.6, gorfo:{a:5, s:41.1, b:0.002573, c:"abq"}, q="Mark", p=0}
674 *
675 * left right diff name/type
676 * --------------------------------------------------------
677 * 5 5 0 foo::int
678 * 123.5 123.6 0.1 bar::float
679 * gorfo::{
680 * 5 5 0 a::int
681 * <?> 41.1 s::float
682 * 0.002567 0.002573 0.000006 b::float
683 * "abc" "abq" "==!" c::string
684 * }
685 * "mark" "Mark" "!===" q::string
686 * 0 0 0 p::int
687 *
688 * As an object... holds only the difference 'values'
689 * {foo:0, bar:0.1, gorfo:{a:0, b:0.0006, c:"==!", additions:{s:41.1}}, q:"!===", p:0}
690 *
691 *
692 * null boolean int double string extpr array object
693 *
694 * null ok REV REV REV REV REV REV REV
695
696 * boolean ok ok REV REV REV REV REV REV
697
698 * int ok non-zero ok REV REV REV REV REV
699
700 * double ok non-zero cast ok REV REV REV REV
701
702 * string ok non-empty strtol strtod ok REV REV REV
703
704 * extpr trunc non-empty size=1 size=1 chars ok REV REV
705
706 * array trunc non-empty size=1 size=1 chars diff ok REV
707
708 * object trunc non-empty size=1 size=1 ??? diff diff MAIN
709 *
710 * There is a 2D array of function pointers, diff_matrix_funcs, which holds pointers
711 * to all functions. The REV functions are just calls to the symmetric method with
712 * left/right reversed and a flag to indicate a reversal was made.
713 *
714 * The object/object method is main entry point for caller. The object/array and object/
715 * object methods are also called recursively. Extptr is not a recursive call. Note that
716 * things like object names, depth of recursion, reversal flag, etc. need to get passed
717 * recusively. This stuff is not appropriate for caller to worry about so need some kind
718 * of wrapper method for caller that turns around and calls the main recursive entry
719 * here.
720 *
721 * An object/non-object method is a clear diff. If diff-only is desired return value, then
722 * execution is complete at that point. If inclusive only, the execution is complete then
723 * too. Otherwise, it would descend recursively on the object and null on the other.
724 *
725 * When does recursion complete? When both objects' precedence <= 4;
726 *
727 * Need to know when diffs are strict or not (is a boolean 1 same as an int 1, depends
728 * on strictness)?
729 *
730 * Naming of functions in the matrix
731 *
732 * json_diff_<left-type>_<right-type>();
733 *
734 * json_diff_null_null()
735 * json_diff_null_bool(), e.g. json_diff_bool_null is a REV-wrapper to json_diff_null_bool()
736 * json_diff_null_int()
737 * json_diff_null_double()
738 * json_diff_null_string()
739 * json_diff_null_extptr()
740 * json_diff_null_array() will have to recurse on array members
741 * json_diff_null_object() will have to recurse on object members but so too for int_object
742 * and double_object, etc. Thats a lot of duplicated code. We need to punt here.
743 *
744 * More involved methods
745 *
746 * json_diff_extptr_extptr() /* non-recursive but like an int/int or double/double or maybe int/double */
747 * json_diff_extptr_array() /* left is a 'datatype'd number; right is a json object which might be a number */
748 * json_diff_extptr_object()
749 * json_diff_array_array() /* recurses on both left and right */
750 * json_diff_array_object() /* recurses on both left and right */
751 * json_diff_object_object() /* recurses on both left and right */
752
753 *
754 * Args these methods need to take...
755 * left object, right object, left-name, right-name,
756 * depth, mode, flags (incl rev), tolerances, retval
757 *
758 * Caller's call to wrapper turns around and uses "LEFT-OBJECT" "RIGHT-OBJECT" names.
759 * depth=0, mode, flags and tolerances determined by caller.
760 *
761 * Need a way to set/push return 'value' (int 0/1, printbuf-str, object). Might be
762 * best as macro'd code. At the point where you know you have a diff of some kind
763 * you then 'handle-diff' which may be an early return, printing to a buffer or
764 * constructing 'additions'/'deletions' of an object. Macro'd code allows for
765 * logic handling early return to be written only once.
766 *
767 *
768 */
769
770 #define HANDLE_DIFF()
771 { \
772 int *ret_intp = (int *) retval;
773 struct printbuf *ret_pbuf = (struct printbuf *) retval;
774 struct json_object *ret_obj = (struct json_object *) retval;
775
776 if (mode == json_diff_bool)
777 {
778 *ret_intp = is_diff;
779 }
780 else if (mode == json_diff_string)
781 {
782 if (is_diff || (flags & JSON_C_DIFF_TOTAL))
783 {
784 if (flags & JSON_C_DIFF_REVERSE_LR)
785 {
786 }
787 else
788 {
789 if (flags & JSON_C_DIFF_RADIX_DECIMAL)
790 sprintbuf(ret_pbuf, "%s%-20s int %20d%20d%20d\n", indent[depth], name, lval, rval, dval);
791 else if (flags & JSON_C_DIFF_RADIX_BINARY)
792 }
793 }
794 }
795 else if (mode == json_diff_object)
796 {
797 if (is_diff || (flags & JSON_C_DIFF_TOTAL))
798 {
799 if (flags & JSON_C_DIFF_REVERS_LR)
800 {
801 }
802 else
803 {
804 if (json_object_get_type(lobj) == json_type_null &&
805 json_object_get_type(robj) != json_type_null)
806 {
807 struct json_object *adds_obj;
808 if (!json_object_object_get_ex(ret_obj, "additions", &adds_obj))
809 {
810 adds_obj = json_object_new_object();
811 json_object_object_add(ret_obj, "additions", adds_obj);
812 }
813
814 /* add robj to additions */
815 json_object_object_add(adds_obj, rkey, robj);
816 }
817 else if (json_object_get_type(lobj) != json_type_null &&
818 json_object_get_type(robj) == json_type_null)
819 {
820 struct json_object *dels_obj;
821 if (!json_object_object_get_ex(ret_obj, "deletions", &dels_obj))
822 {
823 dels_obj = json_object_new_object();
824 json_object_object_add(ret_obj, "deletions", dels_obj);
825 }
826
827 /* add lobj deletions */
828 json_object_object_add(dels_obj, lkey, lobj);
829 }
830 else
831 {
832 /* add normal */
833 json_object_object_add(ret_obj, lkey, diff_obj);
834 }
835 }
836 }
837 }
838 }
839
840 void json_diff_null_null(
841 struct json_object const *lobj, char const *lkey,
842 struct json_object const *robj, char const *rkey,
843 int depth, enum json_diff_mode dmode, int flags,
844 double const tols[3], void *retval)
845 {
846 HANDLE_DIFF();
847 }
848
849 void json_diff_int_int(
850 struct json_object const *lobj, char const *ltag,
851 struct json_object const *robj, char const *rtag,
852 int depth, enum json_diff_mode dmode, int flags,
853 double const tols[3], void *retval)
854 {
855 int lval = json_object_get_int(lobj);
856 int rval = json_object_get_int(robj);
857 int dval = lval - rval;
858
859 if (flags & JSON_C_DIFF_REVERSE_LR) dval = -dval;
860
861 is_diff = DBIsDifferentLongLong(lval, rval, tols[0], tols[1], tols[2]);
862
863 HANDLE_DIFF();
864 }
865
866
867
868
869
870 void json_object_int_diff(
871 int depth, char const *tag,
872 struct json_object *lobj, struct json_object *robj,
873 enum json_diff_mode mode, int flags, double const tols[3],
874 void *retthing)
875 {
876 int *ret_intp = (int *) retthing;
877 struct printbuf *ret_pbufp = (struct printbuf *) retthing;
878 struct json_object *ret_objp = (struct json_object *) retthing;
879 int lval, rval;
880 double abstol, reltol, reltol_eps;
881
882 if (retthing == 0) return;
883
884 if (flags & JSON_C_DIFF_ZERO_TOLS)
885 tols[0] = tols[1] = tols[2] = 0;
886
887 abstol = tols[0];
888 reltol = tols[1];
889 retol_eps = tols[2];
890
891 lval = json_object_get_int(lobj);
892 rval = json_object_get_int(robj);
893 dval = lval - rval;
894
895 is_diff = DBIsDifferentDouble(lval, rval, abstol, reltol, reltol_eps);
896
897 switch (mode)
898 {
899 case json_diff_bool:
900 *ret_intp = is_diff;
901 return;
902 case json_diff_string:
903 if (is_diff || (flags & JSON_C_DIFF_TOTAL))
904 sprintbuf(*ret_pbp, "%s%-20s int %20d%20d%20d\n", indent[depth], name, lval, rval, dval);
905 return;
906 case json_diff_object:
907 if (is_diff || (flags & JSON_C_DIFF_TOTAL))
908 json_object_object_add(ret_objp, name, json_object_new_int(dval));
909 return;
910 }
911 }
912
913 /* Return type precedence of json object according to generality of type */
914 int json_object_type_precedence(struct json_object *obj)
915 {
916 switch (json_object_get_type(obj))
917 {
918 case json_type_null: return 0; /* primitive */
919 case json_type_boolean: return 1; /* primitive */
920 case json_type_int: return 2; /* primitive */
921 case json_type_double: return 3; /* primitive */
922 case json_type_string: return 4; /* primitive */
923 case json_type_array: return 6; /* recursive */
924 case json_type_object:
925 {
926 if (json_object_is_extptr(obj)) return 5; /* primitive */
927 return 7; /* recursive */
928 }
929 }
930 return -1;
931 }
932
933 void json_object_diff(struct json_object *lobj, struct json_object *robj,
934 enum json_diff_mode dmode, int flags, double const tols[3],
935 void *retval)
936 {
937 if (retval == 0) return;
938
939 /* zero the tolerances if we need to */
940 if (flags & JSON_C_DIFF_ZERO_TOLS)
941 tols[0] = tols[1] = tols[2] = 0;
942
943 lprec = json_type_precedence(lobj);
944 rprec = json_type_precedence(robj);
945
946 /* Call correct json_object differencing function */
947 (*diff_matrix_func)(lobj, robj,
948
949
950
951
952 }
953
954 void json_object_diff(
955 int depth,
956 struct json_object *lobj, char const *lnm,
957 struct json_object *robj, char const *rnm,
958 enum json_diff_mode mode, int flags, double const tols[3],
959 void *retthing)
960 {
961 int *ret_intp = (int *) retthing;
962 struct printbuf *ret_pbufp = (struct printbuf *) retthing;
963 struct json_object *ret_objp = (struct json_object *) retthing;
964
965 if (json_object_is_type(lobj, json_type_object))
966 {
967 if (json_object_is_type(robj, json_type_object))
968 {
969 struct json_object_iterator liter, lend;
970 struct json_object_iterator riter, rend;
971
972 /* First, iterate over lobj members */
973 liter = json_object_iter_begin(lobj);
974 lend = json_object_iter_end(lobj);
975 while (!json_object_iter_equal(&liter, &lend))
976 {
977 char const *lsubnm = json_object_iter_peek_name(&liter);
978 struct json_object *lsubobj = json_object_iter_peek_value(&liter);
979 struct json_object *rsubobj;
980
981 if (json_object_object_get_ex(robj, lsubnm, &rsubojb))
982 {
983 /* Recurse on the two object's members */
984 json_object_diff(depth+1,lsubobj,lsubnm,rsubobj,lsubnm,mode,flags,tols,retting);
985 if (mode == json_diff_bool && *ret_intp != 0) return;
986 }
987 else if (!(flags&JSON_DIFF_EXCLUSIVE_ONLY))
988 {
989 if (mode == json_diff_bool)
990 {
991 *ret_intp = 1;
992 return;
993 }
994 json_object_diff(depth+1,lsubobj,lsubnm,0,"***DEL***",mode,flags,tols,retting);
995 }
996 }
997
998 /* At this point, we've examined robj only for those names in lobj.
999 * So, now, iterate over robj members we haven't already visited.
1000 * Anything we encounter at this point is treated as an 'addition.' */
1001 if (!(flags&JSON_DIFF_EXCLUSIVE_ONLY))
1002 {
1003 riter = json_object_iter_begin(robj);
1004 rend = json_object_iter_end(robj);
1005 while (!json_object_iter_equal(&riter, &rend))
1006 {
1007 char const *rsubnm = json_object_iter_peek_name(&riter);
1008 if (json_object_object_get_ex(lobj, rsubnm, 0)) continue;
1009
1010 if (mode == json_diff_bool)
1011 {
1012 *ret_intp = 1;
1013 return;
1014 }
1015 json_object_diff(depth+1,0,"***ADD***",rsubobj,rsubnm,mode,flags,tols,retting);
1016 }
1017 }
1018 }
1019 else if (!(flags&JSON_DIFF_MIXED_TYPE))
1020 {
1021 if (mode == json_diff_bool)
1022 {
1023 *ret_intp = 1;
1024 return;
1025 }
1026 else if (mode == json_diff_string)
1027 {
1028 }
1029 else
1030 {
1031 }
1032 }
1033 else if (json_object_is_type(robj, json_type_array))
1034 {
1035 }
1036 else
1037 {
1038 }
1039 }
1040 else if (json_object_is_type(lobj, json_type_array))
1041 {
1042 if (json_object_is_type(robj, json_type_array))
1043 {
1044 int llen = json_object_array_length(lobj);
1045 int rlen = json_object_array_length(robj);
1046 int done = 0, i = 0;
1047 while (!done)
1048 {
1049 struct json_object *lsubobj = json_object_array_get_idx(lobj, i);
1050 struct json_object *rsubobj = json_object_array_get_idx(robj, i);
1051 if (i < llen && i < rlen)
1052 {
1053 char subnm[32];
1054 snprintf(subnm, sizeof(subnm), "[%03d]", i);
1055
1056 /* Recurse on the two object's members */
1057 json_object_diff(depth+1,lsubobj,subnm,rsubobj,subnm,mode,flags,tols,retting);
1058 if (mode == json_diff_bool && *ret_intp != 0) return;
1059 }
1060 else if (i < llen && !(flags&JSON_DIFF_EXCLUSIVE_ONLY)) /* We're off the end of robj array */
1061 {
1062 if (mode == json_diff_bool)
1063 {
1064 *ret_intp = 1;
1065 return;
1066 }
1067 json_object_diff(depth+1,lsubobj,subnm,0,"***DEL***",mode,flags,tols,retting);
1068 }
1069 else if (!(flags&JSON_DIFF_EXCLUSIV_ONLY))/* We're off the end of lobj array */
1070 {
1071 if (mode == json_diff_bool)
1072 {
1073 *ret_intp = 1;
1074 return;
1075 }
1076 json_object_diff(depth+1,0,"***ADD***",rsubobj,subnm,mode,flags,tols,retting);
1077 }
1078 }
1079 }
1080 else
1081 {
1082 }
1083 }
1084 else if (json_object_is_type(robj, json_type_array))
1085 {
1086 }
1087 }
1088
1089 void json_object_object_diff(
1090 int depth,
1091 struct json_object *left, char const *lnm,
1092 struct json_object *right, char const *rnm,
1093 enum json_diff_mode mode, int flags, double const tols[3],
1094 void *retthing)
1095 {
1096
1097 int *ret_intp = (int *) retthing;
1098 struct printbuf *ret_pbufp = (struct printbuf *) retthing;
1099 struct json_object *ret_objp = (struct json_object *) retthing;
1100
1101 if (retthing == 0) return;
1102
1103 if (flags & JSON_C_DIFF_ZERO_TOLS)
1104 tols[0] = tols[1] = tols[2] = 0;
1105
1106 if (json_object_get_type(left) == json_object_get_type(right))
1107 {
1108 /* Diffing two objects of the same, primitive type is easy */
1109 switch (json_object_get_type(left))
1110 {
1111 case json_type_null:
1112 {
1113 switch (mode)
1114 {
1115 case json_diff_bool:
1116 *ret_intp = 0;
1117 return;
1118 case json_diff_string:
1119 if (flags & JSON_C_DIFF_TOTAL)
1120 sprintbuf(*ret_pbp, "%s%-20s null %20d%20d%20d\n", indent[depth], lnm, 0, 0, 0);
1121 return;
1122 case json_diff_object:
1123 if (flags & JSON_C_DIFF_TOTAL)
1124 json_object_object_add(ret_objp, lnm, json_object_new_int(0));
1125 return;
1126 }
1127 }
1128 case json_type_bool:
1129 {
1130 switch (mode)
1131 {
1132 int is_diff = json_object_is_boolean_diff(left, right);
1133 case json_diff_bool:
1134 *ret_intp = is_diff;
1135 return;
1136 case json_diff_string:
1137 if (is_diff || (flags & JSON_C_DIFF_TOTAL))
1138 sprintbuf(*ret_pbp, "%s%-20s bool %20d%20d%20d\n", indent[depth],
1139 lnm, json_object_get_boolean(left), json_object_get_boolean(right),
1140 json_object_get_boolean_diff_string(left, right));
1141 return;
1142 case json_diff_object:
1143 if (is_diff || (flags & JSON_C_DIFF_TOTAL))
1144 json_object_object_add(ret_objp, lnm, json_object_get_boolean_diff_object(left, right));
1145 return;
1146 }
1147 }
1148 case json_type_int:
1149 {
1150 int is_diff = json_object_is_int_diff(left, right, tols[0], tols[1], tols[2]);
1151 switch (mode)
1152 {
1153 case json_diff_bool:
1154 *ret_intp = is_diff;
1155 return;
1156 case json_diff_string:
1157 if (is_diff || (flags & JSON_C_DIFF_TOTAL))
1158 sprintbuf(*ret_pbp, "%s%-20s int %20d%20d%20d\n", indent[depth],
1159 lnm, json_object_get_int(left), json_object_get_int(right),
1160 json_object_get_int_diff_string(left, right, tols[0], tols[1], tols[2]));
1161 case json_diff_object:
1162 if (is_diff || (flags & JSON_C_DIFF_TOTAL))
1163 json_object_object_add(ret_objp, lnm, json_object_get_int_diff_object(left, right, tols[0], tols[1], tols[2]));
1164 }
1165 }
1166 case json_type_double:
1167 {
1168 int is_diff = json_object_is_double_diff(left, right, tols[0], tols[1], tols[2]);
1169 switch (mode)
1170 {
1171 case json_diff_bool:
1172 *ret_intp = is_diff;
1173 return;
1174 case json_diff_string:
1175 if (is_diff || (flags & JSON_C_DIFF_TOTAL))
1176 sprintbuf(*ret_pbp, "%s%-20s double %16g%16g%16g\n", indent[depth],
1177 lnm, json_object_get_double(left), json_object_get_double(right),
1178 json_object_get_double_diff_string(left, right, tols[0], tols[1], tols[2]));
1179 return;
1180 case json_diff_object:
1181 if (is_diff || (flags & JSON_C_DIFF_TOTAL))
1182 json_object_object_add(ret_objp, lnm, json_object_get_double_diff_object(left, right, tols[0], tols[1], tols[2]));
1183 return;
1184 }
1185 }
1186 case json_type_string:
1187 {
1188 }
1189 case json_type_array:
1190 {
1191 int is_diff = json_object_is_array_diff(left, right, tols[0], tols[1], tols[2]);
1192 switch (mode)
1193 {
1194 case json_diff_bool:
1195 *ret_intp = is_diff;
1196 return;
1197 case json_diff_string:
1198 if (is_diff || (flags & JSON_C_DIFF_TOTAL))
1199 {
1200 int leftcnt = json_object_array_length(left);
1201 int rightcnt = json_object_array_length(right);
1202 int mincnt = leftcnt < rightcnt ? leftcnt : rightcnt;
1203 int maxcnt = leftcnt < rightcnt ? rightcnt : leftcnt;
1204 sprintbuf(*ret_pbp, "%s%-20s array\n", indent[depth], lnm);
1205 for (i = 0 i < mincnt; i++)
1206 {
1207 char const subnm[32];
1208 json_object struct *subleft = json_object_array_get_idx(left, i);
1209 json_object struct *subright = json_object_array_get_idx(right, i);
1210 sprintf(subnm, "%s[%03d]", lnm, i);
1211 json_object_object_diff(depth+1, subleft, subright, subnm, subnm, mode, flags, tols, retthing);
1212 }
1213 if (!(flags & JSON_C_DIFF_INCLUSIVE))
1214 {
1215 for (i = mincnt; i < maxcnt; i++)
1216 {
1217 char const subnm[32];
1218 json_object struct *subleft = json_object_array_get_idx(left, i);
1219 json_object struct *subright = json_object_array_get_idx(right, i);
1220 sprintf(subnm, "%s[%03d]", lnm, i);
1221 json_object_object_diff(depth+1, subleft, subright, subnm, subnm, mode, flags, tols, retthing);
1222 }
1223 }
1224 }
1225 return;
1226 case json_diff_object:
1227 if (is_diff || (flags & JSON_C_DIFF_TOTAL))
1228 {
1229 }
1230 return;
1231 }
1232 }
1233 case json_type_object:
1234 {
1235 }
1236 }
1237 }
1238 else if (flags & JSON_C_DIFF_MIX_PRIM_TYPE)
1239 {
1240
1241
1242
1243
1244
1245
1246 }
1247 else
1248 {
1249 }
1250
1251
1252
1253
1254
1255
1256 struct printbuf *pb_retval = printbuf_new(); /* prepare for pb return */
1257 struct json_object *jso_retval = json_object_new(); /* prepare for obj return */
1258 }
1259
1260 int json_object_get_boolean_diff(struct json_object *left, struct json_object *right)
1261 {
1262 json_bool leftval, rightval;
1263
1264 if (!json_object_is_type(left, json_type_boolean) ||
1265 !json_object_is_type(right, json_type_boolean))
1266 return -2; /* invalid diff */
1267
1268 leftval = json_object_get_boolean(left);
1269 rightval = json_object_get_boolean(right);
1270
1271 return (int) leftval - (int) rightval;
1272 }
1273
1274 int json_object_get_int_diff(struct json_object *left, struct json_object *right,
1275 double abstol, double reltol, double reltol_eps)
1276 {
1277 int leftval, rightval, diff;
1278
1279 if (!json_object_is_type(left, json_type_int) ||
1280 !json_object_is_type(right, json_type_int))
1281 return -INT_MAX; /* invalid diff */
1282
1283 leftval = json_object_get_int(left);
1284 rightval = json_object_get_int(right);
1285 diff = leftval - rightval;
1286 if (DBIsDifferentDouble(leftval, rightval, abstol, reltol, reltol_eps))
1287 return diff;
1288 return 0;
1289 }
1290
1291 double json_object_get_double_diff(struct json_object *left, struct json_object *right,
1292 double abstol, double reltol, double reltol_eps)
1293 {
1294 double leftval, rightval, diff;
1295
1296 if (!json_object_is_type(left, json_type_double) ||
1297 !json_object_is_type(right, json_type_double))
1298 return -DBL_MAX; /* invalid diff */
1299
1300 leftval = json_object_get_double(left);
1301 rightval = json_object_get_double(right);
1302 diff = leftval - rightval;
1303 if (DBIsDifferentDouble(leftval, rightval, abstol, reltol, reltol_eps))
1304 return diff;
1305 return 0;
1306 }
1307
1308 char const *json_object_get_string_diff(struct json_object *left, struct json_object *right)
1309 {
1310 if (!json_object_is_type(left, json_type_string) ||
1311 !json_object_is_type(right, json_type_string))
1312 return 0; /* invalid diff */
1313
1314 /* MAJOR HACK. To be consistent with rest of json-c interface, we would like to return
1315 * a garbage collected string here. But, we're not going to create any new json
1316 * objects here. So, our solution is to use the left operand's printbuf to hold
1317 * the returned string. This is 'ok' because any future attempt to to_json_string()
1318 * on the left operand will reset the left opeand's printbuf before stringifying the
1319 * left operand json object. */
1320 printbuf_reset(left->_pb);
1321
1322 {
1323 char const *leftval = json_object_get_string(left);
1324 int leftlen = json_object_get_string_len(left);
1325 char const *rightval = json_object_get_string(right);
1326 int rightlen = json_object_get_string_len(right);
1327 int i, hasdiffs = 0, hasextras = 0, maxlen;
1328
1329 maxlen = leftlen < rightlen ? rightlen : leftlen;
1330
1331 for (i = 0; i < maxlen; i++)
1332 {
1333 char leftc = i < leftlen ? leftval[i] : '<';
1334 char rightc = i < rightlen ? rightval[i] : '>';
1335
1336 if (i < leftlen && i < rightlen)
1337 {
1338 if (leftc == rightc)
1339 {
1340 sprintfbuf(left->_pb, "=");
1341 }
1342 else
1343 {
1344 hasdiffs = 1;
1345 sprintfbuf(left->_pb, "!");
1346 }
1347 }
1348 else
1349 {
1350 if (hasextras == 0)
1351 hasextras = i;
1352 if (i < leftlen)
1353 sprintfbuf(left->_pb, "%c", hasdiffs ? '<' : leftc);
1354 else
1355 sprintfbuf(left->_pb, "%c", hasdiffs ? '>' : rightc);
1356 }
1357 }
1358
1359 if (!hasdiffs && !hasextras)
1360 {
1361 printbuf_reset(left->_pb);
1362 return "";
1363 }
1364
1365 return left->_pb->buf;
1366 }
1367 }
1368
1369 struct json_object *json_object_array_get_idx_diff(
1370 struct json_object *left, struct json_object *right, int idx,
1371 double abstol, double reltol, double reltol_eps)
1372 {
1373 struct json_object *leftidx, *rightidx;
1374
1375 if (!json_object_is_type(left, json_type_array) ||
1376 !json_object_is_type(right, json_type_array))
1377 return json_type_null; /* invalid diff */
1378
1379 if (idx >= json_object_array_length(left) ||
1380 idx >= json_object_array_length(right))
1381 return json_type_null; /* invalid diff */
1382
1383 leftidx = json_object_array_get_idx(left, idx);
1384 rightidx = json_object_array_get_idx(right, idx);
1385
1386 return json_object_object_get_diff(leftidx, rightidx);
1387 }
1388
1389 int json_object_is_boolean_diff(struct json_object *left, struct json_object *right)
1390 {
1391 return json_object_get_boolean_diff(left, right) == 0;
1392 }
1393
1394 int json_object_is_int_diff(struct json_object *left, struct json_object *right,
1395 double abstol, double reltol, double reltol_eps)
1396 {
1397 return json_object_get_int_diff(left, right, abstol, reltol, reltol_eps) == 0;
1398 }
1399
1400 int json_object_is_double_diff(struct json_object *left, struct json_object *right,
1401 double abstol, double reltol, double reltol_eps)
1402 {
1403 return json_object_get_double_diff(left, right, abstol, reltol, reltol_eps) == 0;
1404 }
1405
1406 int json_object_is_string_diff(struct json_object *left, struct json_object *right)
1407 {
1408 char const *diffval = json_object_get_string_diff(left, right);
1409 if (!diffval) return 1;
1410 if (strlen(diffval) == 0) return 0;
1411 return 1;
1412 }
1413
1414 char const *
1415 json_object_get_boolean_diff_string(struct json_object *left, struct json_object *right)
1416 {
1417 int diffval = json_object_get_boolean_diff(left, right);
1418 /* MAJOR HACK. To be consistent with rest of json-c interface, we would like to return
1419 * a garbage collected string here. But, we're not going to create any new json
1420 * objects here. So, our solution is to use the left operand's printbuf to hold
1421 * the returned string. This is 'ok' because any future attempt to to_json_string()
1422 * on the left operand will reset the left opeand's printbuf before stringifying the
1423 * left operand json object. */
1424 printbuf_reset(left->_pb);
1425 if (diffval == -2)
1426 sprintbuf(left->_pb, "incompatible objects");
1427 else if (diffval != 0)
1428 sprintbuf(left->_pb, "%d", diffval);
1429 else
1430 sprintbuf(left->_pb, "");
1431 return left->_pb->buf;
1432 }
1433
1434 char const *
1435 json_object_get_int_diff_string(struct json_object *left, struct json_object *right,
1436 double abstol, double reltol, double reltol_eps)
1437 {
1438 int diffval = json_object_get_int_diff(left, right, abstol, reltol, reltol_eps);
1439 /* MAJOR HACK. To be consistent with rest of json-c interface, we would like to return
1440 * a garbage collected string here. But, we're not going to create any new json
1441 * objects here. So, our solution is to use the left operand's printbuf to hold
1442 * the returned string. This is 'ok' because any future attempt to to_json_string()
1443 * on the left operand will reset the left opeand's printbuf before stringifying the
1444 * left operand json object. */
1445 printbuf_reset(left->_pb);
1446 if (diffval == -INT_MAX)
1447 sprintbuf(left->_pb, "incompatible objects");
1448 else if (diffval != 0)
1449 sprintbuf(left->_pb, "%d", diffval);
1450 else
1451 sprintbuf(left->_pb, "");
1452 return left->_pb->buf;
1453 }
1454
1455 char const *
1456 json_object_get_double_diff_string(struct json_object *left, struct json_object *right,
1457 double abstol, double reltol, double reltol_eps)
1458 {
1459 double diffval = json_object_get_double_diff(left, right, abstol, reltol, reltol_eps);
1460 /* MAJOR HACK. To be consistent with rest of json-c interface, we would like to return
1461 * a garbage collected string here. But, we're not going to create any new json
1462 * objects here. So, our solution is to use the left operand's printbuf to hold
1463 * the returned string. This is 'ok' because any future attempt to to_json_string()
1464 * on the left operand will reset the left opeand's printbuf before stringifying the
1465 * left operand json object. */
1466 printbuf_reset(left->_pb);
1467 if (diffval == -DBL_MAX)
1468 sprintbuf(left->_pb, "incompatible objects");
1469 else if (diffval != 0)
1470 sprintbuf(left->_pb, "%16g", diffval);
1471 else
1472 sprintbuf(left->_pb, "");
1473 return left->_pb->buf;
1474 }
1475
1476 char const *
1477 json_object_get_string_diff_string(struct json_object *left, struct json_object *right)
1478 {
1479 return json_object_get_string_diff(left, right);
1480 }
1481
1482
1483
1484
1485 struct json_object *
1486 json_object_get_boolean_diff_object(struct json_object *left, struct json_object *right)
1487 {
1488 return json_object_new_boolean(json_object_get_boolean_diff(left, right));
1489 }
1490
1491 struct json_object *
1492 json_object_get_int_diff_object(struct json_object *left, struct json_object *right,
1493 double abstol, double reltol, double reltol_eps)
1494 {
1495 return json_object_new_int(json_object_get_int_diff(left, right, abstol, reltol, reltol_eps));
1496 }
1497
1498 struct json_object *
1499 json_object_get_double_diff_object(struct json_object *left, struct json_object *right,
1500 double abstol, double reltol, double reltol_eps)
1501 {
1502 return json_object_new_double(json_object_get_double_diff(left, right, abstol, reltol, reltol_eps));
1503 }
1504
1505 struct json_object *
1506 json_object_get_string_diff_object(struct json_object *left, struct json_object *right)
1507 {
1508 return json_object_new_string(json_object_get_string_diff(left, right));
1509 }
1510
1511 struct json_object *json_object_get_array_diff(struct json_object *left, struct json_object *right,
1512 double abstol, double reltol, double reltol_eps)
1513 {
1514 if (!json_object_is_type(left, json_type_array) ||
1515 !json_object_is_type(right, json_type_array))
1516 return json_object_invalid; /* invalid diff */
1517
1518 leftlen = json_object_get_array_length(left);
1519 rightlen = json_object_get_array_length(right);
1520 if (leftlen > rightlen)
1521 {
1522 maxlen = leftlen;
1523 minlen = rightlen;
1524 }
1525 else
1526 {
1527 maxlen = rightlen;
1528 minlen = leftlen;
1529 }
1530
1531 retval = json_object_new_array();
1532
1533 for (i = 0; i < minlen; i++)
1534 {
1535 json_object_array_add(retval, json_object_object_get_diff());
1536 }
1537
1538 }
1539
1540 struct json_object *json_object_object_get_diff(struct json_object *left, struct json_object *right,
1541 double abstol, double reltol, double reltol_eps)
1542 {
1543 if (json_object_get_type(left) != json_object_get_type(right))
1544 return json_object_invalid; /* invalid diff */
1545
1546 switch (json_object_get_type(left))
1547 {
1548 case json_type_null:
1549 {
1550 return json_type_null;
1551 }
1552 case json_type_boolean:
1553 {
1554 return json_object_get_boolean_diff_object(left, right);
1555 }
1556 case json_type_int:
1557 {
1558 return json_object_get_int_diff_object(left, right, abstol, reltol, reltol_eps);
1559 }
1560 case json_type_double:
1561 {
1562 return json_object_get_double_diff_object(left, right, abstol, reltol, reltol_eps);
1563 }
1564 case json_type_string:
1565 {
1566 return json_object_get_string_diff_object(left, right);
1567 }
1568 case json_type_array:
1569 {
1570 return json_object_get_array_diff_object(left, right, abstol, reltol, reltol_eps);
1571 }
1572 case json_type_object:
1573 {
1574 /* check for extptr case here */
1575 }
1576 }
1577 }
1578
1579
1580 int json_object_diff_object(struct json_object *objL, struct json_object *objR)
1581 {
1582 struct json_object_iterator jiter, jend;
1583
1584 jiter = json_object_iter_begin(objL);
1585 jend = json_object_iter_end(objL);
1586 while (!json_object_iter_equal(&jiter, &jend))
1587 {
1588 char const *mnameL = json_object_iter_peek_name(&jiter);
1589 struct json_object *subobjL = json_object_iter_peek_value(&jiter);
1590 struct json_object *subobjR;
1591
1592 if (!json_object_object_get_ex(objR, mnameL, &subobjR))
1593
1594 }
1595 }
1596 #endif
1597
1598 /* Return a Silo object as a Json Object. Bulk data passed using funky exptr */
1599 PUBLIC struct json_object *
DBGetJsonObject(DBfile * dbfile,char const * objname)1600 DBGetJsonObject(DBfile *dbfile, char const *objname)
1601 {
1602 int i;
1603 struct json_object *jobj = json_object_new_object();
1604 DBobject *sobj = DBGetObject(dbfile, objname);
1605 if (!sobj) return jobj;
1606
1607 json_object_object_add(jobj, "silo_name", json_object_new_string(sobj->name));
1608 json_object_object_add(jobj, "silo_type", json_object_new_int(DBGetObjtypeTag(sobj->type)));
1609
1610 for (i = 0; i < sobj->ncomponents; i++)
1611 {
1612 char cat_comp_name[1024];
1613 snprintf(cat_comp_name, sizeof(cat_comp_name), "%s_%s", objname, sobj->comp_names[i]);
1614 if (!strncmp(sobj->pdb_names[i], "'<i>", 4))
1615 {
1616 json_object_object_add(jobj, sobj->comp_names[i],
1617 json_object_new_int(strtol(sobj->pdb_names[i]+4, NULL, 0)));
1618 }
1619 else if (!strncmp(sobj->pdb_names[i], "'<f>", 4))
1620 {
1621 json_object_object_add(jobj, sobj->comp_names[i],
1622 json_object_new_double(strtod(sobj->pdb_names[i]+4, NULL)));
1623 }
1624 else if (!strncmp(sobj->pdb_names[i], "'<d>", 4))
1625 {
1626 json_object_object_add(jobj, sobj->comp_names[i],
1627 json_object_new_double(strtod(sobj->pdb_names[i]+4, NULL)));
1628 }
1629 else if (!strncmp(sobj->pdb_names[i], "'<s>", 4))
1630 {
1631 char tmp[256];
1632 size_t len = strlen(sobj->pdb_names[i])-5;
1633 memset(tmp, 0, sizeof(tmp));
1634 strncpy(tmp, sobj->pdb_names[i]+4, len);
1635 if (DBInqVarExists(dbfile, tmp))
1636 {
1637 if (DBInqVarType(dbfile, tmp) == DB_VARIABLE)
1638 {
1639 void *p;
1640 int ndims, dims[32];
1641 int dtype = DBGetVarType(dbfile, tmp);
1642 ndims = DBGetVarDims(dbfile, tmp, sizeof(dims)/sizeof(dims[0]), dims);
1643 p = DBGetVar(dbfile, tmp);
1644 json_object_object_add(jobj, sobj->comp_names[i],
1645 json_object_new_extptr(p, ndims, dims, dtype));
1646 }
1647 else
1648 {
1649 json_object_object_add(jobj, sobj->comp_names[i],
1650 DBGetJsonObject(dbfile, tmp));
1651 }
1652 }
1653 else
1654 {
1655 json_object_object_add(jobj, sobj->comp_names[i],
1656 json_object_new_string(tmp));
1657 }
1658 }
1659 else if (DBInqVarType(dbfile, cat_comp_name) == DB_VARIABLE)
1660 {
1661 void *p;
1662 int ndims, dims[32];
1663 int dtype = DBGetVarType(dbfile, cat_comp_name);
1664 ndims = DBGetVarDims(dbfile, cat_comp_name, sizeof(dims)/sizeof(dims[0]), dims);
1665 p = DBGetVar(dbfile, cat_comp_name);
1666 json_object_object_add(jobj, sobj->comp_names[i],
1667 json_object_new_extptr(p, ndims, dims, dtype));
1668 }
1669 else if (DBInqVarExists(dbfile, sobj->comp_names[i]))
1670 {
1671 json_object_object_add(jobj, sobj->comp_names[i],
1672 DBGetJsonObject(dbfile, sobj->comp_names[i]));
1673 }
1674 else /* some component we do not know how to handle */
1675 {
1676 json_object_object_add(jobj, sobj->comp_names[i],
1677 json_object_new_string("<unknown>"));
1678 }
1679 }
1680 DBFreeObject(sobj);
1681 return jobj;
1682 }
1683
DBWriteJsonObject(DBfile * dbfile,struct json_object * jobj)1684 int DBWriteJsonObject(DBfile *dbfile, struct json_object *jobj)
1685 {
1686 static int cnt = 0;
1687 int retval;
1688 DBobject *sobj;
1689 char objnm[256];
1690 struct json_object_iterator jiter, jend;
1691 memset(objnm, 0, sizeof(objnm));
1692 if (json_object_object_get_ex(jobj, "silo_type", 0) &&
1693 json_object_object_get_ex(jobj, "silo_name", 0))
1694 {
1695 json_object *silo_type_obj = json_object_object_get(jobj, "silo_type");
1696 json_object *silo_name_obj = json_object_object_get(jobj, "silo_name");
1697 strncpy(objnm, json_object_get_string(silo_name_obj), sizeof(objnm));
1698 sobj = DBMakeObject(objnm,
1699 json_object_get_int(silo_type_obj),
1700 json_object_object_length(jobj)+10
1701 );
1702 }
1703 else
1704 {
1705 snprintf(objnm, sizeof(objnm), "anon_%d", cnt++);
1706 sobj = DBMakeObject(objnm, DB_USERDEF, json_object_object_length(jobj)+10);
1707 }
1708 jiter = json_object_iter_begin(jobj);
1709 jend = json_object_iter_end(jobj);
1710
1711 /* use foreachC macro instead */
1712 while (!json_object_iter_equal(&jiter, &jend))
1713 {
1714 struct json_object *mobj = json_object_iter_peek_value(&jiter);
1715 char const *mname = json_object_iter_peek_name(&jiter);
1716 json_type jtype = json_object_get_type(mobj);
1717
1718 switch (jtype)
1719 {
1720 case json_type_array:
1721 case json_type_null: break;
1722
1723 case json_type_boolean:
1724 case json_type_int:
1725 {
1726 int val = 0;
1727 if (jtype == json_type_boolean && json_object_get_boolean(mobj))
1728 val = 1;
1729 else
1730 val = json_object_get_int(mobj);
1731 DBAddIntComponent(sobj, mname, val);
1732 break;
1733 }
1734 case json_type_double:
1735 {
1736 DBAddDblComponent(sobj, mname, json_object_get_double(mobj));
1737 break;
1738 }
1739 case json_type_string:
1740 {
1741 if (strlen(json_object_get_string(mobj)))
1742 DBAddStrComponent(sobj, mname, json_object_get_string(mobj));
1743 break;
1744 }
1745
1746 /* warning STRDUPS ARE LEAKS */
1747
1748 case json_type_object: /* must be extptr array reference */
1749 {
1750 if (json_object_object_get_ex(mobj, "ptr", 0) &&
1751 json_object_object_get_ex(mobj, "datatype", 0) &&
1752 json_object_object_get_ex(mobj, "ndims", 0) &&
1753 json_object_object_get_ex(mobj, "dims", 0))
1754 {
1755 int i;
1756 long dims[32];
1757 void *p = json_object_get_strptr(json_object_object_get(mobj, "ptr"));
1758 int datatype = json_object_get_int(json_object_object_get(mobj, "datatype"));
1759 int ndims = json_object_get_int(json_object_object_get(mobj, "ndims"));
1760 struct json_object *darr = json_object_object_get(mobj, "dims");
1761 for (i = 0; i < ndims; i++)
1762 dims[i] = (long) json_object_get_int(json_object_array_get_idx(darr, i));
1763 DBWriteComponent(dbfile, sobj, mname, strdup(objnm),
1764 db_GetDatatypeString(datatype), p, ndims, dims);
1765 }
1766 else
1767 {
1768 json_object *silo_type_subobj=0, *silo_name_subobj=0;
1769 int has_silo_type, has_silo_name;
1770 char tmp[32];
1771 has_silo_type = json_object_object_get_ex(mobj, "silo_type", &silo_type_subobj);
1772 has_silo_name = json_object_object_get_ex(mobj, "silo_name", &silo_name_subobj);
1773 snprintf(tmp, sizeof(tmp), "anon_%d", cnt);
1774
1775 if (has_silo_name && has_silo_type)
1776 DBAddStrComponent(sobj, mname, strdup(json_object_get_string(silo_name_subobj)));
1777 else
1778 DBAddStrComponent(sobj, mname, strdup(tmp));
1779 DBWriteJsonObject(dbfile, mobj);
1780 }
1781
1782 break;
1783 }
1784 }
1785 json_object_iter_next(&jiter);
1786 }
1787 retval = DBWriteObject(dbfile, sobj, 0);
1788 DBFreeObject(sobj);
1789 return retval;
1790 }
1791