The open source OpenXR runtime
1// Copyright 2019-2020, Collabora, Ltd.
2// SPDX-License-Identifier: BSL-1.0
3/*!
4 * @file
5 * @brief Tiny JSON wrapper around cJSON.
6 * @author Jakob Bornecrantz <jakob@collabora.com>
7 * @author Rylie Pavlik <rylie.pavlik@collabora.com>
8 * @ingroup aux_util
9 */
10
11#include "util/u_json.h"
12#ifndef XRT_HAVE_SYSTEM_CJSON
13#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
14#define _CRT_SECURE_NO_WARNINGS
15#endif
16#endif
17
18#include "util/u_logging.h"
19
20#include <assert.h>
21#include <stdio.h>
22
23#ifndef XRT_HAVE_SYSTEM_CJSON
24#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
25#define _CRT_SECURE_NO_WARNINGS
26#endif
27// This includes the c file completely.
28#include "cjson/cJSON.c"
29#endif
30
31
32/*!
33 * Less typing.
34 */
35static inline const cJSON *
36get(const cJSON *json, const char *f)
37{
38 return cJSON_GetObjectItemCaseSensitive(json, f);
39}
40
41const cJSON *
42u_json_get(const cJSON *json, const char *f)
43{
44 return get(json, f);
45}
46
47bool
48u_json_get_string_into_array(const cJSON *json, char *out_str, size_t max_size)
49{
50 assert(out_str != NULL);
51
52 if (!json) {
53 return false;
54 }
55 if (!cJSON_IsString(json)) {
56 return false;
57 }
58
59 int ret = snprintf(out_str, max_size, "%s", json->valuestring);
60 if (ret < 0) {
61 U_LOG_E("Printing string failed: %d", ret);
62 return false;
63 }
64 if ((size_t)ret < max_size) {
65 return true;
66 }
67 U_LOG_E("String size %d is bigger than available %zu", ret, max_size);
68 return false;
69}
70
71bool
72u_json_get_bool(const cJSON *json, bool *out_bool)
73{
74 assert(out_bool != NULL);
75
76 if (!json) {
77 return false;
78 }
79 if (!cJSON_IsBool(json)) {
80 return false;
81 }
82
83 *out_bool = cJSON_IsTrue(json);
84
85 return true;
86}
87
88bool
89u_json_get_int(const cJSON *json, int *out_int)
90{
91 assert(out_int != NULL);
92
93 if (!json) {
94 return false;
95 }
96 if (!cJSON_IsNumber(json)) {
97 return false;
98 }
99
100 *out_int = json->valueint;
101
102 return true;
103}
104
105bool
106u_json_get_double(const cJSON *json, double *out_double)
107{
108 assert(out_double != NULL);
109
110 if (!json) {
111 return false;
112 }
113 if (!cJSON_IsNumber(json)) {
114 return false;
115 }
116 *out_double = json->valuedouble;
117 return true;
118}
119
120bool
121u_json_get_float(const cJSON *json, float *out_float)
122{
123 assert(out_float != NULL);
124
125 double d = 0;
126 if (!u_json_get_double(json, &d)) {
127 return false;
128 }
129
130 *out_float = (float)d;
131 return true;
132}
133
134bool
135u_json_get_vec3(const cJSON *json, struct xrt_vec3 *out_vec3)
136{
137 assert(out_vec3 != NULL);
138
139 if (!json) {
140 return false;
141 }
142 if (!cJSON_IsObject(json)) {
143 return false;
144 }
145
146 struct xrt_vec3 ret;
147 if (!u_json_get_float(get(json, "x"), &ret.x)) {
148 return false;
149 }
150 if (!u_json_get_float(get(json, "y"), &ret.y)) {
151 return false;
152 }
153 if (!u_json_get_float(get(json, "z"), &ret.z)) {
154 return false;
155 }
156
157 *out_vec3 = ret;
158
159 return true;
160}
161
162bool
163u_json_get_vec3_array(const cJSON *json, struct xrt_vec3 *out_vec3)
164{
165 assert(out_vec3 != NULL);
166
167 if (!json) {
168 return false;
169 }
170 if (!cJSON_IsArray(json)) {
171 return false;
172 }
173
174 if (cJSON_GetArraySize(json) != 3) {
175 return false;
176 }
177
178 float array[3] = {0, 0, 0};
179 const cJSON *item = NULL;
180 size_t i = 0;
181 cJSON_ArrayForEach(item, json)
182 {
183 assert(cJSON_IsNumber(item));
184 array[i] = (float)item->valuedouble;
185 ++i;
186 if (i == 3) {
187 break;
188 }
189 }
190
191 out_vec3->x = array[0];
192 out_vec3->y = array[1];
193 out_vec3->z = array[2];
194
195 return true;
196}
197
198bool
199u_json_get_vec3_f64_array(const cJSON *json, struct xrt_vec3_f64 *out_vec3)
200{
201 assert(out_vec3 != NULL);
202
203 if (!json) {
204 return false;
205 }
206 if (!cJSON_IsArray(json)) {
207 return false;
208 }
209
210 if (cJSON_GetArraySize(json) != 3) {
211 return false;
212 }
213
214 double array[3] = {0, 0, 0};
215 const cJSON *item = NULL;
216 size_t i = 0;
217 cJSON_ArrayForEach(item, json)
218 {
219 assert(cJSON_IsNumber(item));
220 array[i] = item->valuedouble;
221 ++i;
222 if (i == 3) {
223 break;
224 }
225 }
226
227 out_vec3->x = array[0];
228 out_vec3->y = array[1];
229 out_vec3->z = array[2];
230
231 return true;
232}
233
234bool
235u_json_get_quat(const cJSON *json, struct xrt_quat *out_quat)
236{
237 assert(out_quat != NULL);
238
239 if (!json) {
240 return false;
241 }
242 if (!cJSON_IsObject(json)) {
243 return false;
244 }
245
246 struct xrt_quat ret;
247 if (!u_json_get_float(get(json, "w"), &ret.w)) {
248 return false;
249 }
250 if (!u_json_get_float(get(json, "x"), &ret.x)) {
251 return false;
252 }
253 if (!u_json_get_float(get(json, "y"), &ret.y)) {
254 return false;
255 }
256 if (!u_json_get_float(get(json, "z"), &ret.z)) {
257 return false;
258 }
259
260 *out_quat = ret;
261
262 return true;
263}
264
265// note: you should be using "position" and "orientation" and lower-case xyz(w)
266bool
267u_json_get_pose(const cJSON *json, struct xrt_pose *out_pose)
268{
269 struct xrt_pose tmp;
270
271 bool good = true;
272 good = good && u_json_get_vec3(u_json_get(json, "position"), &tmp.position);
273 good = good && u_json_get_quat(u_json_get(json, "orientation"), &tmp.orientation);
274
275 if (good) {
276 *out_pose = tmp;
277 }
278 return good;
279}
280
281bool
282u_json_get_pose_permissive(const cJSON *json, struct xrt_pose *out_pose)
283{
284 struct xrt_pose tmp;
285
286 const char *position_names[] = {"position", "translation", "location", "pos", "loc"};
287 const char *orientation_names[] = {"orientation", "rotation", "rot"};
288
289 bool found_position = false;
290
291 for (uint32_t i = 0; i < ARRAY_SIZE(position_names); i++) {
292 found_position = u_json_get_vec3(u_json_get(json, position_names[i]), &tmp.position);
293 if (found_position) {
294 break;
295 }
296 }
297 if (!found_position) {
298 return false;
299 }
300
301 bool found_orientation = false;
302
303 for (uint32_t i = 0; i < ARRAY_SIZE(orientation_names); i++) {
304 found_orientation = u_json_get_vec3(u_json_get(json, orientation_names[i]), &tmp.position);
305 if (found_orientation) {
306 break;
307 }
308 }
309 if (!found_orientation) {
310 return false;
311 }
312
313
314 return true;
315}
316
317
318size_t
319u_json_get_float_array(const cJSON *json_array, float *out_array, size_t max_size)
320{
321 assert(out_array != NULL);
322
323 if (!json_array) {
324 return 0;
325 }
326 if (!cJSON_IsArray(json_array)) {
327 return 0;
328 }
329
330 size_t i = 0;
331 const cJSON *elt;
332 cJSON_ArrayForEach(elt, json_array)
333 {
334 if (i >= max_size) {
335 break;
336 }
337
338 if (!u_json_get_float(elt, &out_array[i])) {
339 U_LOG_W(
340 "u_json_get_float_array got a non-number in a "
341 "numeric array");
342 return i;
343 }
344
345 i++;
346 }
347
348 return i;
349}
350
351size_t
352u_json_get_double_array(const cJSON *json_array, double *out_array, size_t max_size)
353{
354 assert(out_array != NULL);
355
356 if (!json_array) {
357 return 0;
358 }
359 if (!cJSON_IsArray(json_array)) {
360 return 0;
361 }
362
363 size_t i = 0;
364 const cJSON *elt;
365 cJSON_ArrayForEach(elt, json_array)
366 {
367 if (i >= max_size) {
368 break;
369 }
370
371 if (!u_json_get_double(elt, &out_array[i])) {
372 U_LOG_W(
373 "u_json_get_double_array got a non-number in a "
374 "numeric array");
375 return i;
376 }
377
378 i++;
379 }
380
381 return i;
382}
383
384size_t
385u_json_get_int_array(const cJSON *json_array, int *out_array, size_t max_size)
386{
387 assert(out_array != NULL);
388
389 if (!json_array) {
390 return 0;
391 }
392 if (!cJSON_IsArray(json_array)) {
393 return 0;
394 }
395
396 size_t i = 0;
397 const cJSON *elt;
398 cJSON_ArrayForEach(elt, json_array)
399 {
400 if (i >= max_size) {
401 break;
402 }
403
404 if (!u_json_get_int(elt, &out_array[i])) {
405 U_LOG_W(
406 "u_json_get_int got a non-number in a "
407 "numeric array");
408 return i;
409 }
410
411 i++;
412 }
413
414 return i;
415}
416
417bool
418u_json_get_matrix_3x3(const cJSON *json, struct xrt_matrix_3x3 *out_matrix)
419{
420 assert(out_matrix != NULL);
421
422 if (!json) {
423 return false;
424 }
425 if (cJSON_GetArraySize(json) != 3) {
426 return false;
427 }
428
429 size_t total = 0;
430 const cJSON *vec = NULL;
431 cJSON_ArrayForEach(vec, json)
432 {
433 assert(cJSON_GetArraySize(vec) == 3);
434 const cJSON *elem = NULL;
435 cJSON_ArrayForEach(elem, vec)
436 {
437 // Just in case.
438 if (total >= 9) {
439 break;
440 }
441
442 assert(cJSON_IsNumber(elem));
443 out_matrix->v[total++] = (float)elem->valuedouble;
444 }
445 }
446
447 return true;
448}