jim2.h 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  1. // Jim 2.0
  2. //
  3. // Current version of Jim. Main differences from Jim 1.0 are
  4. // - Using Dynamic Arrays for scopes allowing them to be arbitrarily nested,
  5. // - Collecting the output into a sink which is a String Builder now, delegating all the IO hustle to the user of the library,
  6. // - Lack of Jim_Error mechanism, which dealt with IO errors and invalid usage of the API. Since we don't deal with IO anymore we have no IO errors. And invalid usage of the API is simply assert()-ed.
  7. #ifndef JIM_H_
  8. #define JIM_H_
  9. #ifndef JIM_SCOPES_CAPACITY
  10. #define JIM_SCOPES_CAPACITY 128
  11. #endif // JIM_SCOPES_CAPACITY
  12. #include <assert.h>
  13. #include <stdlib.h>
  14. #include <stdbool.h>
  15. #include <string.h>
  16. typedef enum {
  17. JIM_ARRAY_SCOPE,
  18. JIM_OBJECT_SCOPE,
  19. } Jim_Scope_Kind;
  20. typedef struct {
  21. Jim_Scope_Kind kind;
  22. int tail; // Not the first element in an array or an object
  23. int key; // An object key was just placed
  24. } Jim_Scope;
  25. typedef struct {
  26. char *sink;
  27. size_t sink_count;
  28. size_t sink_capacity;
  29. Jim_Scope *scopes;
  30. size_t scopes_count;
  31. size_t scopes_capacity;
  32. size_t pp;
  33. } Jim;
  34. void jim_begin(Jim *jim);
  35. void jim_null(Jim *jim);
  36. void jim_bool(Jim *jim, int boolean);
  37. void jim_integer(Jim *jim, long long int x);
  38. // TODO: deprecate this version of jim_float introduce the one that does not require precision and uses something like sprintf from libc to render the floats
  39. void jim_float(Jim *jim, double x, int precision);
  40. void jim_string(Jim *jim, const char *str);
  41. void jim_string_sized(Jim *jim, const char *str, size_t size);
  42. void jim_element_begin(Jim *jim);
  43. void jim_element_end(Jim *jim);
  44. void jim_array_begin(Jim *jim);
  45. void jim_array_end(Jim *jim);
  46. void jim_object_begin(Jim *jim);
  47. void jim_member_key(Jim *jim, const char *str);
  48. void jim_member_key_sized(Jim *jim, const char *str, size_t size);
  49. void jim_object_end(Jim *jim);
  50. #endif // JIM_H_
  51. #ifdef JIM_IMPLEMENTATION
  52. static void jim_scope_push(Jim *jim, Jim_Scope_Kind kind)
  53. {
  54. if (jim->scopes_count >= jim->scopes_capacity) {
  55. if (jim->scopes_capacity == 0) jim->scopes_capacity = JIM_SCOPES_CAPACITY;
  56. else jim->scopes_capacity *= 2;
  57. jim->scopes = realloc(jim->scopes, sizeof(*jim->scopes)*jim->scopes_capacity);
  58. assert(jim->scopes);
  59. }
  60. jim->scopes[jim->scopes_count].kind = kind;
  61. jim->scopes[jim->scopes_count].tail = 0;
  62. jim->scopes[jim->scopes_count].key = 0;
  63. jim->scopes_count += 1;
  64. }
  65. static void jim_scope_pop(Jim *jim)
  66. {
  67. assert(jim->scopes_count > 0);
  68. jim->scopes_count--;
  69. }
  70. static Jim_Scope *jim_current_scope(Jim *jim)
  71. {
  72. if (jim->scopes_count > 0) {
  73. return &jim->scopes[jim->scopes_count - 1];
  74. }
  75. return NULL;
  76. }
  77. static void jim_write(Jim *jim, const char *buffer, size_t size)
  78. {
  79. while (jim->sink_count + size >= jim->sink_capacity) {
  80. // TODO: rename JIM_SCOPES_CAPACITY to something else since it's used by both sink and scopes
  81. if (jim->sink_capacity == 0) jim->sink_capacity = JIM_SCOPES_CAPACITY;
  82. else jim->sink_capacity *= 2;
  83. jim->sink = realloc(jim->sink, sizeof(*jim->sink)*jim->sink_capacity);
  84. }
  85. memcpy(jim->sink + jim->sink_count, buffer, size);
  86. jim->sink_count += size;
  87. }
  88. static void jim_write_cstr(Jim *jim, const char *cstr)
  89. {
  90. jim_write(jim, cstr, strlen(cstr));
  91. }
  92. static int jim_get_utf8_char_len(unsigned char ch)
  93. {
  94. if ((ch & 0x80) == 0) return 1;
  95. switch (ch & 0xf0) {
  96. case 0xf0:
  97. return 4;
  98. case 0xe0:
  99. return 3;
  100. default:
  101. return 2;
  102. }
  103. }
  104. void jim_begin(Jim *jim)
  105. {
  106. jim->sink_count = 0;
  107. jim->scopes_count = 0;
  108. }
  109. void jim_element_begin(Jim *jim)
  110. {
  111. Jim_Scope *scope = jim_current_scope(jim);
  112. if (scope) {
  113. if (scope->tail && !scope->key) {
  114. jim_write_cstr(jim, ",");
  115. }
  116. if (jim->pp) {
  117. if (scope->key) {
  118. jim_write_cstr(jim, " ");
  119. } else {
  120. jim_write_cstr(jim, "\n");
  121. for (size_t i = 0; i < jim->scopes_count*jim->pp; ++i) {
  122. jim_write_cstr(jim, " ");
  123. }
  124. }
  125. }
  126. }
  127. }
  128. void jim_element_end(Jim *jim)
  129. {
  130. Jim_Scope *scope = jim_current_scope(jim);
  131. if (scope) {
  132. scope->tail = 1;
  133. scope->key = 0;
  134. }
  135. }
  136. void jim_null(Jim *jim)
  137. {
  138. jim_element_begin(jim);
  139. jim_write_cstr(jim, "null");
  140. jim_element_end(jim);
  141. }
  142. void jim_bool(Jim *jim, int boolean)
  143. {
  144. jim_element_begin(jim);
  145. if (boolean) {
  146. jim_write_cstr(jim, "true");
  147. } else {
  148. jim_write_cstr(jim, "false");
  149. }
  150. jim_element_end(jim);
  151. }
  152. static void jim_integer_no_element(Jim *jim, long long int x)
  153. {
  154. if (x < 0) {
  155. jim_write_cstr(jim, "-");
  156. x = -x;
  157. }
  158. if (x == 0) {
  159. jim_write_cstr(jim, "0");
  160. } else {
  161. char buffer[64];
  162. size_t count = 0;
  163. while (x > 0) {
  164. buffer[count++] = (x % 10) + '0';
  165. x /= 10;
  166. }
  167. for (size_t i = 0; i < count / 2; ++i) {
  168. char t = buffer[i];
  169. buffer[i] = buffer[count - i - 1];
  170. buffer[count - i - 1] = t;
  171. }
  172. jim_write(jim, buffer, count);
  173. }
  174. }
  175. void jim_integer(Jim *jim, long long int x)
  176. {
  177. jim_element_begin(jim);
  178. jim_integer_no_element(jim, x);
  179. jim_element_end(jim);
  180. }
  181. static int is_nan_or_inf(double x)
  182. {
  183. unsigned long long int mask = (1ULL << 11ULL) - 1ULL;
  184. return (((*(unsigned long long int*) &x) >> 52ULL) & mask) == mask;
  185. }
  186. void jim_float(Jim *jim, double x, int precision)
  187. {
  188. if (is_nan_or_inf(x)) {
  189. jim_null(jim);
  190. } else {
  191. jim_element_begin(jim);
  192. jim_integer_no_element(jim, (long long int) x);
  193. x -= (double) (long long int) x;
  194. while (precision-- > 0) {
  195. x *= 10.0;
  196. }
  197. jim_write_cstr(jim, ".");
  198. long long int y = (long long int) x;
  199. if (y < 0) {
  200. y = -y;
  201. }
  202. jim_integer_no_element(jim, y);
  203. jim_element_end(jim);
  204. }
  205. }
  206. static void jim_string_sized_no_element(Jim *jim, const char *str, size_t size)
  207. {
  208. const char *hex_digits = "0123456789abcdef";
  209. const char *specials = "btnvfr";
  210. const char *p = str;
  211. size_t len = size;
  212. jim_write_cstr(jim, "\"");
  213. size_t cl;
  214. for (size_t i = 0; i < len; i++) {
  215. unsigned char ch = ((unsigned char *) p)[i];
  216. if (ch == '"' || ch == '\\') {
  217. jim_write(jim, "\\", 1);
  218. jim_write(jim, p + i, 1);
  219. } else if (ch >= '\b' && ch <= '\r') {
  220. jim_write(jim, "\\", 1);
  221. jim_write(jim, &specials[ch - '\b'], 1);
  222. } else if (0x20 <= ch && ch <= 0x7F) { // is printable
  223. jim_write(jim, p + i, 1);
  224. } else if ((cl = jim_get_utf8_char_len(ch)) == 1) {
  225. jim_write(jim, "\\u00", 4);
  226. jim_write(jim, &hex_digits[(ch >> 4) % 0xf], 1);
  227. jim_write(jim, &hex_digits[ch % 0xf], 1);
  228. } else {
  229. jim_write(jim, p + i, cl);
  230. i += cl - 1;
  231. }
  232. }
  233. jim_write_cstr(jim, "\"");
  234. }
  235. void jim_string_sized(Jim *jim, const char *str, size_t size)
  236. {
  237. jim_element_begin(jim);
  238. jim_string_sized_no_element(jim, str, size);
  239. jim_element_end(jim);
  240. }
  241. void jim_string(Jim *jim, const char *str)
  242. {
  243. jim_string_sized(jim, str, strlen(str));
  244. }
  245. void jim_array_begin(Jim *jim)
  246. {
  247. jim_element_begin(jim);
  248. jim_write_cstr(jim, "[");
  249. jim_scope_push(jim, JIM_ARRAY_SCOPE);
  250. }
  251. void jim_array_end(Jim *jim)
  252. {
  253. Jim_Scope *scope = jim_current_scope(jim);
  254. if (jim->pp && scope && scope->tail) {
  255. jim_write_cstr(jim, "\n");
  256. for (size_t i = 0; i < (jim->scopes_count - 1)*jim->pp; ++i) {
  257. jim_write_cstr(jim, " ");
  258. }
  259. }
  260. jim_write_cstr(jim, "]");
  261. jim_scope_pop(jim);
  262. jim_element_end(jim);
  263. }
  264. void jim_object_begin(Jim *jim)
  265. {
  266. jim_element_begin(jim);
  267. jim_write_cstr(jim, "{");
  268. jim_scope_push(jim, JIM_OBJECT_SCOPE);
  269. }
  270. void jim_member_key(Jim *jim, const char *str)
  271. {
  272. jim_member_key_sized(jim, str, strlen(str));
  273. }
  274. void jim_member_key_sized(Jim *jim, const char *str, size_t size)
  275. {
  276. jim_element_begin(jim);
  277. Jim_Scope *scope = jim_current_scope(jim);
  278. assert(scope);
  279. assert(scope->kind == JIM_OBJECT_SCOPE);
  280. assert(!scope->key);
  281. jim_string_sized_no_element(jim, str, size);
  282. jim_write_cstr(jim, ":");
  283. scope->key = 1;
  284. }
  285. void jim_object_end(Jim *jim)
  286. {
  287. Jim_Scope *scope = jim_current_scope(jim);
  288. if (jim->pp && scope && scope->tail) {
  289. jim_write_cstr(jim, "\n");
  290. for (size_t i = 0; i < (jim->scopes_count - 1)*jim->pp; ++i) {
  291. jim_write_cstr(jim, " ");
  292. }
  293. }
  294. jim_write_cstr(jim, "}");
  295. jim_scope_pop(jim);
  296. jim_element_end(jim);
  297. }
  298. #endif // JIM_IMPLEMENTATION