hipack-writer.c 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376
  1. /*
  2. * hipack-writer.c
  3. * Copyright (C) 2015 Adrian Perez <aperez@igalia.com>
  4. *
  5. * Distributed under terms of the MIT license.
  6. */
  7. #include "hipack.h"
  8. /*
  9. * Define FPCONV_H to avoid fpconv/src/fpconv.h being included.
  10. * By making our own definition of the function here, it can be
  11. * marked as "static inline".
  12. */
  13. #define FPCONV_H 1
  14. static inline int fpconv_dtoa (double fp, char dest[24]);
  15. #include "fpconv/src/fpconv.c"
  16. static inline bool
  17. writechar (hipack_writer_t *writer, int ch)
  18. {
  19. assert (ch != HIPACK_IO_ERROR);
  20. assert (ch != HIPACK_IO_EOF);
  21. assert (writer->putchar);
  22. int ret = (*writer->putchar) (writer->putchar_data, ch);
  23. assert (ret != HIPACK_IO_EOF);
  24. return ret == HIPACK_IO_ERROR;
  25. }
  26. #define CHECK_IO(statement) \
  27. do { \
  28. if (statement) return true; \
  29. } while (0)
  30. static inline void
  31. moreindent (hipack_writer_t *writer)
  32. {
  33. if (writer->indent != HIPACK_WRITER_COMPACT)
  34. writer->indent++;
  35. }
  36. static inline void
  37. lessindent (hipack_writer_t *writer)
  38. {
  39. if (writer->indent != HIPACK_WRITER_COMPACT)
  40. writer->indent--;
  41. }
  42. static inline bool
  43. writeindent (hipack_writer_t *writer)
  44. {
  45. int32_t num_spaces = (writer->indent != HIPACK_WRITER_COMPACT)
  46. ? writer->indent * 2 : 0;
  47. while (num_spaces--) {
  48. CHECK_IO (writechar (writer, ' '));
  49. }
  50. return false;
  51. }
  52. static inline bool
  53. writedata (hipack_writer_t *writer,
  54. const char *data,
  55. uint32_t length)
  56. {
  57. if (length) {
  58. assert (data);
  59. while (length--) {
  60. CHECK_IO (writechar (writer, *data++));
  61. }
  62. }
  63. return false;
  64. }
  65. static inline int
  66. mapdigit (unsigned n)
  67. {
  68. if (n < 10) {
  69. return '0' + n;
  70. }
  71. if (n < 36) {
  72. return 'A' + (n - 10);
  73. }
  74. assert (false);
  75. return '?';
  76. }
  77. static inline bool
  78. formatint (hipack_writer_t *writer,
  79. int value,
  80. uint8_t base)
  81. {
  82. assert (writer);
  83. if (value >= base) {
  84. CHECK_IO (formatint (writer, value / base, base));
  85. }
  86. CHECK_IO (writechar (writer, mapdigit (value % base)));
  87. return false;
  88. }
  89. bool
  90. hipack_write_bool (hipack_writer_t *writer,
  91. const bool value)
  92. {
  93. assert (writer);
  94. if (value) {
  95. return writedata (writer, "True", 4);
  96. } else {
  97. return writedata (writer, "False", 5);
  98. }
  99. }
  100. bool
  101. hipack_write_integer (hipack_writer_t *writer,
  102. const int32_t value)
  103. {
  104. assert (writer);
  105. if (value < 0) {
  106. CHECK_IO (writechar (writer, '-'));
  107. return formatint (writer, -value, 10);
  108. } else {
  109. return formatint (writer, value, 10);
  110. }
  111. }
  112. bool
  113. hipack_write_float (hipack_writer_t *writer,
  114. const double value)
  115. {
  116. assert (writer);
  117. char buf[24];
  118. int nchars = fpconv_dtoa (value, buf);
  119. bool need_dot = true;
  120. for (int i = 0; i < nchars; i++) {
  121. CHECK_IO (writechar (writer, buf[i]));
  122. if (buf[i] == '.' || buf[i] == 'e' || buf[i] == 'E') {
  123. need_dot = false;
  124. }
  125. }
  126. if (need_dot) {
  127. CHECK_IO (writechar (writer, '.'));
  128. CHECK_IO (writechar (writer, '0'));
  129. }
  130. return false;
  131. }
  132. bool
  133. hipack_write_string (hipack_writer_t *writer,
  134. const hipack_string_t *hstr)
  135. {
  136. assert (writer);
  137. assert (hstr);
  138. CHECK_IO (writechar (writer, '"'));
  139. for (uint32_t i = 0; i < hstr->size; i++) {
  140. switch (hstr->data[i]) {
  141. case 0x09: /* Horizontal tab. */
  142. CHECK_IO (writechar (writer, '\\'));
  143. CHECK_IO (writechar (writer, 't'));
  144. break;
  145. case 0x0A: /* New line. */
  146. CHECK_IO (writechar (writer, '\\'));
  147. CHECK_IO (writechar (writer, 'n'));
  148. break;
  149. case 0x0D: /* Carriage return. */
  150. CHECK_IO (writechar (writer, '\\'));
  151. CHECK_IO (writechar (writer, 'r'));
  152. break;
  153. case 0x22: /* Double quote. */
  154. CHECK_IO (writechar (writer, '\\'));
  155. CHECK_IO (writechar (writer, '"'));
  156. break;
  157. case 0x5C: /* Backslash. */
  158. CHECK_IO (writechar (writer, '\\'));
  159. CHECK_IO (writechar (writer, '\\'));
  160. break;
  161. default:
  162. if (hstr->data[i] < 0x20) {
  163. /* ASCII non-printable character. */
  164. CHECK_IO (writechar (writer, '\\'));
  165. if ((uint8_t) hstr->data[i] < 16) {
  166. /* Leading zero. */
  167. CHECK_IO (writechar (writer, '0'));
  168. }
  169. CHECK_IO (formatint (writer, (uint8_t) hstr->data[i], 16));
  170. } else {
  171. CHECK_IO (writechar (writer, hstr->data[i]));
  172. }
  173. }
  174. }
  175. CHECK_IO (writechar (writer, '"'));
  176. return false;
  177. }
  178. static bool
  179. write_keyval (hipack_writer_t *writer,
  180. const hipack_dict_t *dict)
  181. {
  182. const hipack_string_t *key;
  183. hipack_value_t *value;
  184. uint32_t pending = hipack_dict_size (dict);
  185. HIPACK_DICT_FOREACH (dict, key, value) {
  186. writeindent (writer);
  187. /* Key */
  188. for (uint32_t i = 0; i < key->size; i++) {
  189. CHECK_IO (writechar (writer, key->data[i]));
  190. }
  191. if (value->annot) {
  192. if (writer->indent == HIPACK_WRITER_COMPACT) {
  193. CHECK_IO (writechar (writer, ':'));
  194. } else {
  195. CHECK_IO (writechar (writer, ' '));
  196. }
  197. } else {
  198. switch (value->type) {
  199. case HIPACK_INTEGER:
  200. case HIPACK_FLOAT:
  201. case HIPACK_BOOL:
  202. case HIPACK_STRING:
  203. CHECK_IO (writechar (writer, ':'));
  204. break;
  205. case HIPACK_DICT:
  206. case HIPACK_LIST:
  207. /* No colon. */
  208. break;
  209. default:
  210. assert (false);
  211. }
  212. if (writer->indent != HIPACK_WRITER_COMPACT) {
  213. CHECK_IO (writechar (writer, ' '));
  214. }
  215. }
  216. CHECK_IO (hipack_write_value (writer, value));
  217. if (writer->indent != HIPACK_WRITER_COMPACT) {
  218. CHECK_IO (writechar (writer, '\n'));
  219. } else if (--pending > 0) {
  220. CHECK_IO (writechar (writer, ','));
  221. }
  222. }
  223. return false;
  224. }
  225. bool
  226. hipack_write_list (hipack_writer_t *writer,
  227. const hipack_list_t *list)
  228. {
  229. assert (writer);
  230. assert (list);
  231. CHECK_IO (writechar (writer, '['));
  232. if (hipack_list_size (list)) {
  233. if (writer->indent != HIPACK_WRITER_COMPACT) {
  234. CHECK_IO (writechar (writer, '\n'));
  235. }
  236. moreindent (writer);
  237. for (uint32_t i = 0; i < list->size;) {
  238. CHECK_IO (writeindent (writer));
  239. CHECK_IO (hipack_write_value (writer, &list->data[i++]));
  240. if (writer->indent != HIPACK_WRITER_COMPACT) {
  241. CHECK_IO (writechar (writer, '\n'));
  242. } else if (i < list->size) {
  243. CHECK_IO (writechar (writer, ','));
  244. }
  245. }
  246. lessindent (writer);
  247. CHECK_IO (writeindent (writer));
  248. }
  249. CHECK_IO (writechar (writer, ']'));
  250. return false;
  251. }
  252. bool
  253. hipack_write_dict (hipack_writer_t *writer,
  254. const hipack_dict_t *dict)
  255. {
  256. CHECK_IO (writechar (writer, '{'));
  257. if (hipack_dict_size (dict)) {
  258. if (writer->indent != HIPACK_WRITER_COMPACT) {
  259. CHECK_IO (writechar (writer, '\n'));
  260. }
  261. moreindent (writer);
  262. CHECK_IO (write_keyval (writer, dict));
  263. lessindent (writer);
  264. CHECK_IO (writeindent (writer));
  265. }
  266. CHECK_IO (writechar (writer, '}'));
  267. return false;
  268. }
  269. bool
  270. hipack_write_value (hipack_writer_t *writer,
  271. const hipack_value_t *value)
  272. {
  273. assert (writer);
  274. assert (value);
  275. if (value->annot) {
  276. const hipack_string_t *key;
  277. hipack_value_t *dummy_value;
  278. HIPACK_DICT_FOREACH (value->annot, key, dummy_value) {
  279. CHECK_IO (writechar (writer, ':'));
  280. for (uint32_t i = 0; i < key->size; i++) {
  281. CHECK_IO (writechar (writer, key->data[i]));
  282. }
  283. }
  284. CHECK_IO (writechar (writer, ' '));
  285. }
  286. switch (value->type) {
  287. case HIPACK_INTEGER:
  288. return hipack_write_integer (writer, value->v_integer);
  289. case HIPACK_FLOAT:
  290. return hipack_write_float (writer, value->v_float);
  291. case HIPACK_BOOL:
  292. return hipack_write_bool (writer, value->v_bool);
  293. case HIPACK_STRING:
  294. return hipack_write_string (writer, value->v_string);
  295. case HIPACK_LIST:
  296. return hipack_write_list (writer, value->v_list);
  297. case HIPACK_DICT:
  298. return hipack_write_dict (writer, value->v_dict);
  299. }
  300. assert (false); /* Never reached. */
  301. return false;
  302. }
  303. bool
  304. hipack_write (hipack_writer_t *writer,
  305. const hipack_dict_t *message)
  306. {
  307. assert (writer);
  308. assert (message);
  309. if (writer->indent != HIPACK_WRITER_COMPACT) {
  310. writer->indent = HIPACK_WRITER_INDENTED;
  311. }
  312. return write_keyval (writer, message);
  313. }
  314. int
  315. hipack_stdio_putchar (void* fp, int ch)
  316. {
  317. assert (fp);
  318. int ret = fputc (ch, (FILE*) fp);
  319. return (ret == EOF) ? HIPACK_IO_ERROR : ch;
  320. }