Adaptive Framework  0.9.0
All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Modules Pages
afw_ubjson_from_value.c
Go to the documentation of this file.
1 // See the 'COPYING' file in the project root for licensing information.
2 /*
3  * AFW UBJSON convert from value
4  *
5  * Copyright (c) 2010-2023 Clemson University
6  *
7  */
8 
14 #include "afw.h"
15 #include "afw_endian.h"
16 #include "afw_ubjson.h"
17 #include <apr_buckets.h>
18 
19 
20 typedef struct from_value_wa_s {
21  afw_xctx_t *xctx;
22  const afw_pool_t *p;
23  const afw_object_options_t *options;
24  afw_boolean_t optimized;
25  afw_boolean_t suppressDataType;
26  void * context;
27  afw_write_cb_t callback;
28  int object_depth;
30 
31 
32 AFW_DEFINE_STATIC_INLINE(void) impl_putc(from_value_wa_t *wa, afw_byte_t c)
33 {
34  char ch;
35 
36  ch = c;
37  wa->callback(wa->context, &ch, 1, wa->p, wa->xctx);
38 }
39 
40 
41 AFW_DEFINE_STATIC_INLINE(void) impl_puts(from_value_wa_t *wa,
42  const afw_utf8_z_t *s)
43 {
44  wa->callback(wa->context, s, strlen(s), wa->p, wa->xctx);
45 }
46 
47 
48 AFW_DEFINE_STATIC_INLINE(void) impl_write(from_value_wa_t *wa,
49  const void * buffer, afw_size_t size)
50 {
51  wa->callback(wa->context, buffer, size, wa->p, wa->xctx);
52 }
53 
54 
55 static from_value_wa_t * create_from_value_wa(
56  const afw_pool_t *p, afw_xctx_t *xctx)
57 {
58  from_value_wa_t *wa;
59 
61  wa->xctx = xctx;
62  wa->p = p;
63 
64  return wa;
65 }
66 
67 static void convert_value_to_ubjson(
68  from_value_wa_t * wa,
69  const afw_value_t * value);
70 
71 static void convert_list_to_ubjson(
72  from_value_wa_t * wa,
73  const afw_list_t * list);
74 
75 static void convert_object_to_ubjson(
76  from_value_wa_t * wa,
77  const afw_object_t * obj);
78 
79 static void convert_string_to_ubjson(
80  from_value_wa_t * wa,
81  const afw_utf8_t *string,
82  afw_boolean_t marker);
83 
84 static void convert_integer_to_ubjson(
85  from_value_wa_t * wa,
86  afw_integer_t i);
87 
88 static void convert_string_to_ubjson(
89  from_value_wa_t * wa,
90  const afw_utf8_t *string,
91  afw_boolean_t marker)
92 {
93  if (string->len == 1 && marker) {
94  /* for single-byte strings, treat as CHAR */
95  impl_putc(wa, AFW_UBJSON_MARKER_CHAR);
96  impl_putc(wa, *(string->s));
97 
98  } else {
99  /* property names do not need a marker */
100  if (marker) {
101  impl_putc(wa, AFW_UBJSON_MARKER_STRING);
102  }
103 
104  convert_integer_to_ubjson(wa, string->len);
105 
106  /* may not be necessary, but not sure if APR handles zero byte writes (empty string) */
107  if (string->len > 0) {
108  impl_write(wa, string->s, string->len);
109  }
110  }
111 }
112 
113 /* this routine takes a 64-bit integer and fits it into the minimal space */
114 static void convert_integer_to_ubjson(
115  from_value_wa_t * wa,
116  afw_integer_t i)
117 {
118  /*
119  UBJSON defines 8 numeric value types (to JSON's 1) for allowing
120  highly optimized storage/retrieval of numeric values depending on
121  the necessary precision; in addition to a number of other more
122  optimized representations of JSON values.
123 
124  Type Size Marker Length Payload
125  ----------------------------------------------------------------------
126  int8 2-bytes i No Yes
127  uint8 2-bytes U No Yes
128  int16 3-bytes I No Yes
129  int32 5-bytes l No Yes
130  int64 9-bytes L No Yes
131  float32 5-bytes d No Yes
132  float64 9-bytes D No Yes
133  high-precision 1-byte+ H Yes Yes (if non-empty)
134  */
135  if (i >= -128 && i <= 127) {
136  char x = (char)i;
137 
138  impl_putc(wa, AFW_UBJSON_MARKER_INT8);
139  impl_write(wa, (const char*)&x, sizeof(x));
140  }
141 
142  else if (i >= 0 && i <= 255) {
143  char x = (char)i;
144 
145  impl_putc(wa, AFW_UBJSON_MARKER_UINT8);
146  impl_write(wa, (const char*)&x, sizeof(x));
147  }
148 
149  else if (i >= -32768 && i <= 32767) {
150  afw_uint16_t x = (afw_uint16_t)i;
151  AFW_ENDIAN_NATIVE_TO_BIG_16(&x);
152 
153  impl_putc(wa, AFW_UBJSON_MARKER_INT16);
154  impl_write(wa, (const char*)&x, sizeof(x));
155  }
156 
157  else if (i >= -2147483647 && i <= 2147483647) {
158  afw_uint32_t x = (afw_uint32_t)i;
159  AFW_ENDIAN_NATIVE_TO_BIG_32(&x);
160 
161  impl_putc(wa, AFW_UBJSON_MARKER_INT32);
162  impl_write(wa, (const char*)&x, sizeof(x));
163  }
164 
165  else {
166  /* this should never happen. 64-bit integers can't exceed this range */
167  AFW_THROW_ERROR_Z(general,
168  "Unexpected error in UBJSON converting 64-bit integer.", wa->xctx);
169  }
170 }
171 
172 /* this routine takes a double and turns it into a 64-bit float */
173 static void convert_double_to_ubjson(
174  from_value_wa_t * wa,
175  double d)
176 {
177  /*
178  UBJSON defines 8 numeric value types (to JSON's 1) for allowing
179  highly optimized storage/retrieval of numeric values depending on
180  the necessary precision; in addition to a number of other more
181  optimized representations of JSON values.
182 
183  Type Size Marker Length Payload
184  ----------------------------------------------------------------------
185  int8 2-bytes i No Yes
186  uint8 2-bytes U No Yes
187  int16 3-bytes I No Yes
188  int32 5-bytes l No Yes
189  int64 9-bytes L No Yes
190  float32 5-bytes d No Yes
191  float64 9-bytes D No Yes
192  high-precision 1-byte+ H Yes Yes (if non-empty)
193  */
194 
195  impl_putc(wa, AFW_UBJSON_MARKER_FLOAT64);
196 
198  impl_write(wa, &d, sizeof(d));
199 }
200 
201 static void convert_object_to_ubjson(
202  from_value_wa_t * wa,
203  const afw_object_t * obj)
204 {
205  const afw_iterator_t *property_iterator;
206  const afw_utf8_t *property_name;
207  const afw_value_t *next;
208  const afw_object_t *meta;
209 
210  impl_putc(wa, AFW_UBJSON_MARKER_OBJECT);
211 
212  (wa->object_depth)++;
213 
214  property_iterator = NULL;
215 
216  if (wa->optimized) {
219  }
220 
221  /* If object has meta, convert it first. */
223  wa->options, wa->p, wa->xctx);
224  if (meta) {
225  convert_string_to_ubjson(wa, &afw_s__meta_, AFW_FALSE);
226  convert_object_to_ubjson(wa, meta);
227  }
228 
229  next = afw_object_get_next_property(obj, &property_iterator,
230  &property_name, wa->xctx);
231  if (next) {
232  while (1) {
233  /* property name */
234  convert_string_to_ubjson(wa, property_name, AFW_FALSE);
235 
236  /* property value */
237  convert_value_to_ubjson(wa, next);
238 
239  next = afw_object_get_next_property(obj, &property_iterator,
240  &property_name, wa->xctx);
241 
242  if (!next)
243  break;
244  }
245  }
246 
247  if (!wa->optimized) {
248  impl_putc(wa, AFW_UBJSON_MARKER_OBJECT_);
249  }
250 
251  (wa->object_depth)--;
252 }
253 
254 
255 #ifdef __FIXME_REMOVE_OLD_BAG_CODE__
256 /*
257  * AFW Bags allow us to create specialized UBJSON Array Containers.
258  * By knowing both the number of elements, and the data type of all
259  * elements in the Bag, we can get more efficient storage with the
260  * optimized format:
261  *
262  * [$][type][#][i][count]
263  *
264  * For example, a Bag of 64 Strings starts with:
265  *
266  * [$S#i64
267  *
268  * http://ubjson.org/type-reference/container-types/#optimized-format-example-array
269  *
270  */
271 static void convert_bag_to_ubjson(
272  from_value_wa_t * wa,
273  const afw_value_bag_t * bag)
274 {
275  afw_size_t count;
276  afw_value_evaluated_t *entry;
277  const afw_data_type_t *data_type;
278  const char *value;
279 
280  /* write out the Array marker */
281  impl_putc(wa, AFW_UBJSON_MARKER_ARRAY);
282 
283  if (wa->optimized) {
284  /* followed by the type */
285  impl_putc(wa, AFW_UBJSON_MARKER_TYPE);
286 
287  /* use the data type for ubjson efficiency */
288  data_type = bag->data_type;
289 
290  if (afw_data_type_is_object(data_type)) {
291  impl_putc(wa, AFW_UBJSON_MARKER_OBJECT);
292  } else if (afw_data_type_is_list(data_type)) {
293  impl_putc(wa, AFW_UBJSON_MARKER_ARRAY);
294  } else if (afw_data_type_is_string(data_type)) {
296  impl_putc(wa, AFW_UBJSON_MARKER_STRING);
297  } else if (afw_data_type_is_double(data_type)) {
299  AFW_THROW_ERROR_Z(general,
300  "Not implemented. Cannot determine smallest type for UBJSON", wa->xctx);
301  }
302 
303  /* the bag count can be used to create more efficient ubjson */
304  count = bag->count;
305 
306  /* write out the count */
307  impl_putc(wa, AFW_UBJSON_MARKER_COUNT);
308  convert_integer_to_ubjson(wa, count);
309  }
310 
311  entry = afw_value_evaluated_allocate(bag->data_type,
312  wa->p, wa->xctx);
313 
314  /* now we can traverse the values */
315  for (value = (const char *)&(bag->internal),
316  count = bag->count;
317  count > 0;
318  value += bag->data_type->c_type_size,
319  count--)
320  {
321  memcpy(&entry->internal, value, bag->data_type->c_type_size);
322  if (wa->optimized)
323  wa->suppressDataType = AFW_TRUE;
324 
325  convert_value_to_ubjson(wa, (const afw_value_t *)entry);
326 
327  if (wa->optimized)
328  wa->suppressDataType = AFW_FALSE;
329  }
330 
331  if (!wa->optimized) {
332  impl_putc(wa, AFW_UBJSON_MARKER_ARRAY_);
333  }
334 }
335 #endif
336 
337 
338 
339 static void convert_list_to_ubjson(
340  from_value_wa_t * wa,
341  const afw_list_t * list)
342 {
343  const afw_iterator_t *list_iterator;
344  const afw_value_t *next;
345 
346  impl_putc(wa, AFW_UBJSON_MARKER_ARRAY);
347 
348  if (wa->optimized) {
351  }
352 
353  list_iterator = NULL;
354  next = afw_list_get_next_value(list, &list_iterator,
355  wa->p, wa->xctx);
356 
357  while (next) {
358  convert_value_to_ubjson(wa, next);
359  next = afw_list_get_next_value(list, &list_iterator,
360  wa->p, wa->xctx);
361  }
362 
363  if (!wa->optimized) {
364  impl_putc(wa, AFW_UBJSON_MARKER_ARRAY_);
365  }
366 }
367 
368 static void convert_value_to_ubjson(
369  from_value_wa_t * wa,
370  const afw_value_t * value)
371 {
372  const afw_utf8_t *string;
373  const afw_data_type_t *value_data_type;
374  afw_value_info_t info;
375 
376  /* Change NULL value pointer to null. */
377  if (!value) {
378  value = afw_value_null;
379  }
380 
381  /* Value must be evaluated already. */
383  afw_value_get_info(value, &info, wa->p, wa->xctx);
384  AFW_THROW_ERROR_FZ(general, wa->xctx,
385  "Unevaluated value encountered producing ubjson "
386  "(%" AFW_UTF8_FMT " %" AFW_UTF8_FMT ")",
387  AFW_UTF8_FMT_ARG(info.value_inf_id),
388  AFW_UTF8_FMT_OPTIONAL_ARG(info.detail)
389  );
390  }
391 
392  /* Get data type. */
393  value_data_type = afw_value_get_data_type(value, wa->xctx);
394 
395  if (afw_value_is_list(value)) {
396  convert_list_to_ubjson(wa, afw_value_as_list(value, wa->xctx));
397  }
398 
399  else if (afw_value_is_object(value)) {
400  convert_object_to_ubjson(wa, afw_value_as_object(value, wa->xctx));
401  }
402 
403  else if (afw_value_is_defined_and_evaluated(value)) {
404 
405  /* Primitive json type is null. */
406  if (afw_utf8_equal(&value_data_type->jsonPrimitive,
407  &AFW_JSON_S_PRIMITIVE_NULL))
408  {
409  impl_putc(wa, AFW_UBJSON_MARKER_NULL);
410  }
411 
412  /* Primitive json type is string. */
413  else if (afw_utf8_equal(&value_data_type->jsonPrimitive,
414  &AFW_JSON_S_PRIMITIVE_STRING))
415  {
416  string = afw_value_as_utf8(value, wa->p, wa->xctx);
417  if (!string) {
418  AFW_THROW_ERROR_Z(general, "Error converting string.", wa->xctx);
419  }
420  convert_string_to_ubjson(wa, string, AFW_TRUE);
421  }
422 
423  /* Primitive json type is boolean. */
424  else if (afw_utf8_equal(&value_data_type->jsonPrimitive,
425  &AFW_JSON_S_PRIMITIVE_BOOLEAN))
426  {
427  if (((afw_value_boolean_t*)value)->internal)
428  impl_putc(wa, AFW_UBJSON_MARKER_TRUE);
429  else
430  impl_putc(wa, AFW_UBJSON_MARKER_FALSE);
431  }
432 
433  /* C type is an integer. */
434  else if (afw_utf8_equal_utf8_z(&value_data_type->cType, AFW_DATA_TYPE_CTYPE_Q_integer))
435  {
436  convert_integer_to_ubjson(wa, afw_value_as_integer(value, wa->xctx));
437  }
438 
439  /* C type is double. */
440  else if (afw_utf8_equal_utf8_z(&value_data_type->cType, AFW_DATA_TYPE_CTYPE_Q_double))
441  {
442  convert_double_to_ubjson(wa, afw_value_as_double(value, wa->xctx));
443  }
444 
445  else {
446  AFW_THROW_ERROR_FZ(general, wa->xctx,
447  "Cannot determine suitable encoding for this c type: %.*s",
448  value_data_type->cType.len, value_data_type->cType.s);
449  }
450  }
451 
452  else {
453  AFW_THROW_ERROR_Z(general,
454  "Value type is invalid for this content-type", wa->xctx);
455  }
456 }
457 
458 
459 /* Convert a value to json and write it. */
460 extern void
461 afw_ubjson_internal_write_value(
462  const afw_value_t *value,
463  const afw_object_options_t *options,
464  void * context,
465  afw_write_cb_t callback,
466  const afw_pool_t *p, afw_xctx_t *xctx)
467 {
468  from_value_wa_t *wa;
469 
470  /* Create and initialize workarea and associated resources. */
471  wa = create_from_value_wa(p, xctx);
472  wa->options = options;
473  wa->context = context;
474  wa->callback = callback;
475 
476  /* Convert object to json. */
477  convert_value_to_ubjson(wa, value);
478 }
479 
480 
481 /* Convert a value to ubjson. */
482 const afw_memory_t *
484  const afw_value_t * value,
485  const afw_pool_t *p,
486  afw_xctx_t *xctx)
487 {
488  const afw_memory_writer_t *writer;
489  const afw_memory_t *raw;
490 
491  writer = afw_memory_create_writer(p, xctx);
492 
493  afw_ubjson_internal_write_value(value, NULL,
494  writer->context, writer->callback, p, xctx);
495 
496  raw = afw_memory_writer_retrieve_and_release(writer, xctx);
497  return raw;
498 }
Adaptive Framework Core API.
Adaptive Framework Endian Header.
Header file for Adaptive Framework UBJSON.
#define afw_data_type_is_double(A_DATA_TYPE)
Macro to determine if data type is double.
afw_value_as_double(const afw_value_t *value, afw_xctx_t *xctx)
Typesafe cast of data type double.
#define AFW_DATA_TYPE_CTYPE_Q_double
Quoted c type for data type id double.
#define AFW_DATA_TYPE_CTYPE_Q_integer
Quoted c type for data type id integer.
afw_value_as_integer(const afw_value_t *value, afw_xctx_t *xctx)
Typesafe cast of data type integer.
#define afw_value_is_list(A_VALUE)
Macro to determine if value is evaluated list.
afw_value_as_list(const afw_value_t *value, afw_xctx_t *xctx)
Typesafe cast of data type list.
#define afw_data_type_is_list(A_DATA_TYPE)
Macro to determine if data type is list.
#define afw_value_is_object(A_VALUE)
Macro to determine if value is evaluated object.
#define afw_data_type_is_object(A_DATA_TYPE)
Macro to determine if data type is object.
afw_value_as_object(const afw_value_t *value, afw_xctx_t *xctx)
Typesafe cast of data type object.
#define afw_data_type_is_string(A_DATA_TYPE)
Macro to determine if data type is string.
#define AFW_UTF8_FMT_ARG(A_STRING)
Convenience Macro for use with AFW_UTF8_FMT to specify arg.
Definition: afw_common.h:605
#define AFW_FALSE
Definition: afw_common.h:392
apr_uint16_t afw_uint16_t
16-bit unsigned integer.
Definition: afw_common.h:178
struct afw_iterator_s afw_iterator_t
#define AFW_UTF8_FMT_OPTIONAL_ARG(A_STRING)
Convenience Macro for use with AFW_UTF8_FMT to specify optional arg.
Definition: afw_common.h:616
#define AFW_TRUE
Definition: afw_common.h:383
_Bool afw_boolean_t
Definition: afw_common.h:373
unsigned char afw_byte_t
A byte of memory (unsigned).
Definition: afw_common.h:208
#define AFW_UTF8_FMT
Format string specifier used for afw_utf8_t.
Definition: afw_common.h:588
afw_utf8_octet_t afw_utf8_z_t
NFC normalized UTF-8 null terminated string.
Definition: afw_common.h:523
apr_uint32_t afw_uint32_t
32-bit unsigned integer.
Definition: afw_common.h:184
apr_size_t afw_size_t
size_t.
Definition: afw_common.h:151
afw_size_t(* afw_write_cb_t)(void *context, const void *buffer, afw_size_t size, const afw_pool_t *p, afw_xctx_t *xctx)
Typedef for write callback function.
Definition: afw_common.h:1226
apr_int64_t afw_integer_t
typedef for big signed int.
Definition: afw_common.h:321
#define AFW_THROW_ERROR_FZ(code, xctx, format_z,...)
Macro used to set error and 0 rv in xctx and throw it.
Definition: afw_error.h:319
#define AFW_THROW_ERROR_Z(code, message_z, xctx)
Macro used to set error and 0 rv in xctx and throw it.
Definition: afw_error.h:283
#define afw_list_get_next_value(instance, iterator, p, xctx)
Call method get_next_value of interface afw_list.
afw_memory_writer_retrieve_and_release(const afw_memory_writer_t *writer, afw_xctx_t *xctx)
Retrieve memory as one chunk from memory writer and release writer.
Definition: afw_memory.c:441
afw_memory_create_writer(const afw_pool_t *p, afw_xctx_t *xctx)
Create a memory writer.
Definition: afw_memory.c:377
#define afw_object_get_next_property(instance, iterator, property_name, xctx)
Call method get_next_property of interface afw_object.
const afw_object_t * afw_object_meta_create_accessor_with_options(const afw_object_t *instance, const afw_object_options_t *options, const afw_pool_t *p, afw_xctx_t *xctx)
Create an object instance to access an object's meta with options.
#define afw_pool_calloc_type(instance, type, xctx)
Macro to allocate cleared memory to hold type in pool.
Definition: afw_pool.h:167
const afw_memory_t * afw_ubjson_from_value(const afw_value_t *value, const afw_pool_t *p, afw_xctx_t *xctx)
Convert an adaptive value to UBJSON.
afw_boolean_t afw_utf8_equal_utf8_z(const afw_utf8_t *s1, const afw_utf8_z_t *s2_z)
Check to see if a string equals a utf8_z string.
afw_boolean_t afw_utf8_equal(const afw_utf8_t *s1, const afw_utf8_t *s2)
Check to see if a string equals another string.
#define afw_value_get_data_type(instance, xctx)
Call method get_data_type of interface afw_value.
#define afw_value_get_info(instance, info, p, xctx)
Call method get_info of interface afw_value.
afw_value_as_utf8(const afw_value_t *value, const afw_pool_t *p, afw_xctx_t *xctx)
Definition: afw_value.c:456
afw_value_evaluated_t * afw_value_evaluated_allocate(const afw_data_type_t *data_type, const afw_pool_t *p, afw_xctx_t *xctx)
Allocate function for an evaluated data type value.
#define afw_value_is_defined_and_evaluated(A_VALUE)
Macro to determine if value is defined and evaluated.
Definition: afw_value.h:481
afw_value_null
Adaptive value null.
Definition: afw_value.h:320
Interface afw_data_type public struct.
Interface afw_list public struct.
Struct for memory pointer and size.
Definition: afw_common.h:505
Return value from afw_memory_create_writer().
Definition: afw_memory.h:102
Struct for object processing options.
Interface afw_object public struct.
Interface afw_pool public struct.
NFC normalized UTF-8 string.
Definition: afw_common.h:545
struct for data type boolean values.
Struct to access internal of all evaluated values.
Definition: afw_value.h:60
Filled in by afw_value get_info method.
Definition: afw_value.h:49
Interface afw_value public struct.
Interface afw_xctx public struct.