iodine 0.6.5 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of iodine might be problematic. Click here for more details.

Files changed (98) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +11 -0
  3. data/README.md +4 -4
  4. data/SPEC-Websocket-Draft.md +3 -6
  5. data/bin/mustache.rb +128 -0
  6. data/examples/test_template.mustache +16 -0
  7. data/ext/iodine/fio.c +9397 -0
  8. data/ext/iodine/fio.h +4723 -0
  9. data/ext/iodine/fio_ary.h +353 -54
  10. data/ext/iodine/fio_cli.c +351 -361
  11. data/ext/iodine/fio_cli.h +84 -105
  12. data/ext/iodine/fio_hashmap.h +70 -16
  13. data/ext/iodine/fio_json_parser.h +35 -24
  14. data/ext/iodine/fio_siphash.c +104 -4
  15. data/ext/iodine/fio_siphash.h +18 -2
  16. data/ext/iodine/fio_str.h +1218 -0
  17. data/ext/iodine/fio_tmpfile.h +1 -1
  18. data/ext/iodine/fiobj.h +13 -8
  19. data/ext/iodine/fiobj4sock.h +6 -8
  20. data/ext/iodine/fiobj_ary.c +107 -17
  21. data/ext/iodine/fiobj_ary.h +36 -4
  22. data/ext/iodine/fiobj_data.c +146 -127
  23. data/ext/iodine/fiobj_data.h +25 -23
  24. data/ext/iodine/fiobj_hash.c +7 -7
  25. data/ext/iodine/fiobj_hash.h +6 -5
  26. data/ext/iodine/fiobj_json.c +20 -17
  27. data/ext/iodine/fiobj_json.h +5 -5
  28. data/ext/iodine/fiobj_mem.h +71 -0
  29. data/ext/iodine/fiobj_mustache.c +310 -0
  30. data/ext/iodine/fiobj_mustache.h +40 -0
  31. data/ext/iodine/fiobj_numbers.c +199 -94
  32. data/ext/iodine/fiobj_numbers.h +7 -7
  33. data/ext/iodine/fiobj_str.c +142 -333
  34. data/ext/iodine/fiobj_str.h +65 -55
  35. data/ext/iodine/fiobject.c +49 -11
  36. data/ext/iodine/fiobject.h +40 -39
  37. data/ext/iodine/http.c +382 -190
  38. data/ext/iodine/http.h +124 -80
  39. data/ext/iodine/http1.c +99 -127
  40. data/ext/iodine/http1.h +5 -5
  41. data/ext/iodine/http1_parser.c +3 -2
  42. data/ext/iodine/http1_parser.h +2 -2
  43. data/ext/iodine/http_internal.c +14 -12
  44. data/ext/iodine/http_internal.h +25 -19
  45. data/ext/iodine/iodine.c +37 -18
  46. data/ext/iodine/iodine.h +4 -0
  47. data/ext/iodine/iodine_caller.c +9 -2
  48. data/ext/iodine/iodine_caller.h +2 -0
  49. data/ext/iodine/iodine_connection.c +82 -117
  50. data/ext/iodine/iodine_defer.c +57 -50
  51. data/ext/iodine/iodine_defer.h +0 -1
  52. data/ext/iodine/iodine_fiobj2rb.h +4 -2
  53. data/ext/iodine/iodine_helpers.c +4 -4
  54. data/ext/iodine/iodine_http.c +25 -32
  55. data/ext/iodine/iodine_json.c +2 -1
  56. data/ext/iodine/iodine_mustache.c +423 -0
  57. data/ext/iodine/iodine_mustache.h +6 -0
  58. data/ext/iodine/iodine_pubsub.c +48 -153
  59. data/ext/iodine/iodine_pubsub.h +5 -4
  60. data/ext/iodine/iodine_rack_io.c +7 -5
  61. data/ext/iodine/iodine_store.c +16 -13
  62. data/ext/iodine/iodine_tcp.c +26 -34
  63. data/ext/iodine/mustache_parser.h +1085 -0
  64. data/ext/iodine/redis_engine.c +740 -646
  65. data/ext/iodine/redis_engine.h +13 -15
  66. data/ext/iodine/resp_parser.h +11 -5
  67. data/ext/iodine/websocket_parser.h +13 -13
  68. data/ext/iodine/websockets.c +240 -393
  69. data/ext/iodine/websockets.h +52 -113
  70. data/lib/iodine.rb +1 -1
  71. data/lib/iodine/mustache.rb +140 -0
  72. data/lib/iodine/version.rb +1 -1
  73. metadata +15 -28
  74. data/ext/iodine/defer.c +0 -566
  75. data/ext/iodine/defer.h +0 -148
  76. data/ext/iodine/evio.c +0 -26
  77. data/ext/iodine/evio.h +0 -161
  78. data/ext/iodine/evio_callbacks.c +0 -26
  79. data/ext/iodine/evio_epoll.c +0 -251
  80. data/ext/iodine/evio_kqueue.c +0 -194
  81. data/ext/iodine/facil.c +0 -2325
  82. data/ext/iodine/facil.h +0 -616
  83. data/ext/iodine/fio_base64.c +0 -277
  84. data/ext/iodine/fio_base64.h +0 -71
  85. data/ext/iodine/fio_llist.h +0 -257
  86. data/ext/iodine/fio_mem.c +0 -675
  87. data/ext/iodine/fio_mem.h +0 -143
  88. data/ext/iodine/fio_random.c +0 -248
  89. data/ext/iodine/fio_random.h +0 -45
  90. data/ext/iodine/fio_sha1.c +0 -362
  91. data/ext/iodine/fio_sha1.h +0 -107
  92. data/ext/iodine/fio_sha2.c +0 -842
  93. data/ext/iodine/fio_sha2.h +0 -169
  94. data/ext/iodine/pubsub.c +0 -867
  95. data/ext/iodine/pubsub.h +0 -221
  96. data/ext/iodine/sock.c +0 -1366
  97. data/ext/iodine/sock.h +0 -566
  98. data/ext/iodine/spnlock.inc +0 -111
@@ -2,8 +2,7 @@
2
2
  #include <ruby/encoding.h>
3
3
  #include <ruby/io.h>
4
4
 
5
- #include "evio.h"
6
- #include "facil.h"
5
+ #include "fio.h"
7
6
 
8
7
  /* *****************************************************************************
9
8
  Static stuff
@@ -24,7 +23,7 @@ Raw TCP/IP Protocol
24
23
  #define IODINE_MAX_READ 8192
25
24
 
26
25
  typedef struct {
27
- protocol_s p;
26
+ fio_protocol_s p;
28
27
  VALUE io;
29
28
  } iodine_protocol_s;
30
29
 
@@ -34,11 +33,6 @@ typedef struct {
34
33
  char buffer[IODINE_MAX_READ];
35
34
  } iodine_buffer_s;
36
35
 
37
- /**
38
- * A string to identify the protocol's service.
39
- */
40
- static const char *iodine_tcp_service = "iodine TCP/IP raw connection";
41
-
42
36
  /**
43
37
  * Converts an iodine_buffer_s pointer to a Ruby string.
44
38
  */
@@ -57,21 +51,21 @@ static void *iodine_tcp_on_data_in_GIL(void *b_) {
57
51
  }
58
52
 
59
53
  /** Called when a data is available, but will not run concurrently */
60
- static void iodine_tcp_on_data(intptr_t uuid, protocol_s *protocol) {
54
+ static void iodine_tcp_on_data(intptr_t uuid, fio_protocol_s *protocol) {
61
55
  iodine_buffer_s buffer;
62
- buffer.len = sock_read(uuid, buffer.buffer, IODINE_MAX_READ);
56
+ buffer.len = fio_read(uuid, buffer.buffer, IODINE_MAX_READ);
63
57
  if (buffer.len <= 0) {
64
58
  return;
65
59
  }
66
60
  buffer.io = ((iodine_protocol_s *)protocol)->io;
67
61
  IodineCaller.enterGVL(iodine_tcp_on_data_in_GIL, &buffer);
68
62
  if (buffer.len == IODINE_MAX_READ) {
69
- facil_force_event(uuid, FIO_EVENT_ON_DATA);
63
+ fio_force_event(uuid, FIO_EVENT_ON_DATA);
70
64
  }
71
65
  }
72
66
 
73
67
  /** called when the socket is ready to be written to. */
74
- static void iodine_tcp_on_ready(intptr_t uuid, protocol_s *protocol) {
68
+ static void iodine_tcp_on_ready(intptr_t uuid, fio_protocol_s *protocol) {
75
69
  iodine_protocol_s *p = (iodine_protocol_s *)protocol;
76
70
  iodine_connection_fire_event(p->io, IODINE_CONNECTION_ON_DRAINED, Qnil);
77
71
  (void)uuid;
@@ -81,15 +75,16 @@ static void iodine_tcp_on_ready(intptr_t uuid, protocol_s *protocol) {
81
75
  * Called when the server is shutting down, immediately before closing the
82
76
  * connection.
83
77
  */
84
- static void iodine_tcp_on_shutdown(intptr_t uuid, protocol_s *protocol) {
78
+ static uint8_t iodine_tcp_on_shutdown(intptr_t uuid, fio_protocol_s *protocol) {
85
79
  iodine_protocol_s *p = (iodine_protocol_s *)protocol;
86
80
  iodine_connection_fire_event(p->io, IODINE_CONNECTION_ON_SHUTDOWN, Qnil);
81
+ return 0;
87
82
  (void)uuid;
88
83
  }
89
84
 
90
85
  /** Called when the connection was closed, but will not run concurrently */
91
86
 
92
- static void iodine_tcp_on_close(intptr_t uuid, protocol_s *protocol) {
87
+ static void iodine_tcp_on_close(intptr_t uuid, fio_protocol_s *protocol) {
93
88
  iodine_protocol_s *p = (iodine_protocol_s *)protocol;
94
89
  iodine_connection_fire_event(p->io, IODINE_CONNECTION_ON_CLOSE, Qnil);
95
90
  free(p);
@@ -97,7 +92,7 @@ static void iodine_tcp_on_close(intptr_t uuid, protocol_s *protocol) {
97
92
  }
98
93
 
99
94
  /** called when a connection's timeout was reached */
100
- static void iodine_tcp_ping(intptr_t uuid, protocol_s *protocol) {
95
+ static void iodine_tcp_ping(intptr_t uuid, fio_protocol_s *protocol) {
101
96
  iodine_protocol_s *p = (iodine_protocol_s *)protocol;
102
97
  iodine_connection_fire_event(p->io, IODINE_CONNECTION_PING, Qnil);
103
98
  (void)uuid;
@@ -121,7 +116,7 @@ static void iodine_tcp_on_finish(intptr_t uuid, void *udata) {
121
116
  * The `on_connect` callback should return a pointer to a protocol object
122
117
  * that will handle any connection related events.
123
118
  *
124
- * Should either call `facil_attach` or close the connection.
119
+ * Should either call `fio_attach` or close the connection.
125
120
  */
126
121
  static void iodine_tcp_on_connect(intptr_t uuid, void *udata) {
127
122
  VALUE handler = (VALUE)udata;
@@ -233,13 +228,12 @@ static VALUE iodine_tcp_listen(VALUE self, VALUE args) {
233
228
  if (rb_port != Qnil) {
234
229
  Check_Type(rb_port, T_STRING);
235
230
  }
236
- if (facil_listen(.port = (rb_port == Qnil ? NULL : StringValueCStr(rb_port)),
237
- .address =
238
- (rb_address == Qnil ? NULL
239
- : StringValueCStr(rb_address)),
240
- .on_open = iodine_tcp_on_open,
241
- .on_finish = iodine_tcp_on_finish,
242
- .udata = (void *)rb_handler) == -1) {
231
+ if (fio_listen(.port = (rb_port == Qnil ? NULL : StringValueCStr(rb_port)),
232
+ .address =
233
+ (rb_address == Qnil ? NULL : StringValueCStr(rb_address)),
234
+ .on_open = iodine_tcp_on_open,
235
+ .on_finish = iodine_tcp_on_finish,
236
+ .udata = (void *)rb_handler) == -1) {
243
237
  IodineStore.remove(rb_handler);
244
238
  rb_raise(rb_eRuntimeError,
245
239
  "failed to listen to requested address, unknown error.");
@@ -289,12 +283,12 @@ static VALUE iodine_tcp_connect(VALUE self, VALUE args) {
289
283
  Check_Type(rb_timeout, T_FIXNUM);
290
284
  timeout = NUM2USHORT(rb_timeout);
291
285
  }
292
- facil_connect(.port = (rb_port == Qnil ? NULL : StringValueCStr(rb_port)),
293
- .address =
294
- (rb_address == Qnil ? NULL : StringValueCStr(rb_address)),
295
- .on_connect = iodine_tcp_on_connect,
296
- .on_fail = iodine_tcp_on_fail, .timeout = timeout,
297
- .udata = (void *)rb_handler);
286
+ fio_connect(.port = (rb_port == Qnil ? NULL : StringValueCStr(rb_port)),
287
+ .address =
288
+ (rb_address == Qnil ? NULL : StringValueCStr(rb_address)),
289
+ .on_connect = iodine_tcp_on_connect,
290
+ .on_fail = iodine_tcp_on_fail, .timeout = timeout,
291
+ .udata = (void *)rb_handler);
298
292
  return rb_handler;
299
293
  (void)self;
300
294
  }
@@ -322,7 +316,7 @@ static VALUE iodine_tcp_attach_fd(VALUE self, VALUE fd, VALUE handler) {
322
316
  if (other == -1) {
323
317
  rb_raise(rb_eIOError, "invalid fd.");
324
318
  }
325
- intptr_t uuid = sock_open(other);
319
+ intptr_t uuid = fio_fd2uuid(other);
326
320
  iodine_tcp_attch_uuid(uuid, handler);
327
321
  IodineStore.remove(handler);
328
322
  return handler;
@@ -355,7 +349,7 @@ Allow uuid attachment
355
349
  /** assigns a protocol and IO object to a handler */
356
350
  void iodine_tcp_attch_uuid(intptr_t uuid, VALUE handler) {
357
351
  if (handler == Qnil || handler == Qfalse || handler == Qtrue) {
358
- sock_close(uuid);
352
+ fio_close(uuid);
359
353
  return;
360
354
  }
361
355
  /* temporary, in case `iodine_connection_new` invokes the GC */
@@ -367,7 +361,6 @@ void iodine_tcp_attch_uuid(intptr_t uuid, VALUE handler) {
367
361
  *p = (iodine_protocol_s){
368
362
  .p =
369
363
  {
370
- .service = iodine_tcp_service,
371
364
  .on_data = iodine_tcp_on_data,
372
365
  .on_ready = NULL /* set only after the on_open callback */,
373
366
  .on_shutdown = iodine_tcp_on_shutdown,
@@ -378,8 +371,7 @@ void iodine_tcp_attch_uuid(intptr_t uuid, VALUE handler) {
378
371
  .arg = p, .handler = handler),
379
372
  };
380
373
  /* clear away (remember the connection object manages these concerns) */
381
- facil_attach(uuid, &p->p);
374
+ fio_attach(uuid, &p->p);
382
375
  iodine_connection_fire_event(p->io, IODINE_CONNECTION_ON_OPEN, Qnil);
383
376
  p->p.on_ready = iodine_tcp_on_ready;
384
- evio_add_write(sock_uuid2fd(uuid), (void *)uuid);
385
377
  }
@@ -0,0 +1,1085 @@
1
+ /*
2
+ Copyright: Boaz Segev, 2018
3
+ License: MIT
4
+
5
+ Feel free to copy, use and enjoy according to the license provided.
6
+ */
7
+ #ifndef H_MUSTACHE_LOADR_H
8
+ /**
9
+ * A mustache parser using a callback systems that allows this implementation to
10
+ * be framework agnostic (i.e., can be used with any JSON library).
11
+ *
12
+ * When including the mustache parser within an iumplementation file,
13
+ * `INCLUDE_MUSTACHE_IMPLEMENTATION` must be defined as 1. This allows the
14
+ * header's types to be exposed within a containing header.
15
+ *
16
+ * The API has three functions:
17
+ *
18
+ * 1. `mustache_load` loads a template file, converting it to instruction data.
19
+ * 2. `mustache_build` calls any callbacks according to the loaded instructions.
20
+ * 3. `mustache_free` frees the instruction and data memory (the template).
21
+ *
22
+ * The template is loaded and converted to an instruction array using
23
+ * `mustache_load`. This loads any nested templates / partials as well.
24
+ *
25
+ * The resulting instruction array (`mustache_s *`) is composed of three memory
26
+ * segments: header segment, instruction array segment and data segment.
27
+ *
28
+ * The instruction array (`mustache_s *`) can be used to build actual output
29
+ * data using the `mustache_build` function.
30
+ *
31
+ * The `mustache_build` function accepts two opaque pointers for user data
32
+ * (`udata1` and `udata2`) that can be used by the callbacks for data input and
33
+ * data output.
34
+ *
35
+ * The `mustache_build` function is thread safe and many threads can build
36
+ * content based on the same template.
37
+ *
38
+ * While the build function is performed, the following callback might be
39
+ * called:
40
+ *
41
+ * * `mustache_on_arg` - called to output an argument's value .
42
+ * * `mustache_on_text` - called to output raw text.
43
+ * * `mustache_on_section_test` - called when a section is tested for validity.
44
+ * * `mustache_on_section_start` - called when entering a named section.
45
+ * * `mustache_on_formatting_error` - called when a formatting error occurred.
46
+ *
47
+ * Once the template is no longer needed, it's easy to free the template using
48
+ * the `mustache_free` function (which, at the moment, simply calls `free`).
49
+ *
50
+ * For details about mustache templating scheme, see: https://mustache.github.io
51
+ *
52
+ */
53
+ #define H_MUSTACHE_LOADR_H
54
+
55
+ #ifndef _GNU_SOURCE
56
+ #define _GNU_SOURCE
57
+ #endif
58
+
59
+ #include <ctype.h>
60
+ #include <stdint.h>
61
+ #include <stdio.h>
62
+ #include <stdlib.h>
63
+ #include <string.h>
64
+ #include <strings.h>
65
+ #include <unistd.h>
66
+
67
+ #include <errno.h>
68
+ #include <fcntl.h>
69
+ #include <sys/stat.h>
70
+ #include <sys/types.h>
71
+
72
+ #if !defined(MUSTACHE_NESTING_LIMIT) || !MUSTACHE_NESTING_LIMIT
73
+ #undef MUSTACHE_NESTING_LIMIT
74
+ #define MUSTACHE_NESTING_LIMIT 64
75
+ #endif
76
+
77
+ #if !defined(__GNUC__) && !defined(__clang__) && !defined(FIO_GNUC_BYPASS)
78
+ #define __attribute__(...)
79
+ #define __has_include(...) 0
80
+ #define __has_builtin(...) 0
81
+ #define FIO_GNUC_BYPASS 1
82
+ #elif !defined(__clang__) && __GNUC__ < 5
83
+ #define __has_builtin(...) 0
84
+ #define FIO_GNUC_BYPASS 1
85
+ #endif
86
+
87
+ #ifndef MUSTACHE_FUNC
88
+ #define MUSTACHE_FUNC static __attribute__((unused))
89
+ #endif
90
+
91
+ /* *****************************************************************************
92
+ Mustache API Functions and Arguments
93
+ ***************************************************************************** */
94
+
95
+ /** an opaque type for mustache template data (when caching). */
96
+ typedef struct mustache_s mustache_s;
97
+
98
+ /** Error reporting (in case of errors). */
99
+ typedef enum mustache_error_en {
100
+ MUSTACHE_OK,
101
+ MUSTACHE_ERR_TOO_DEEP,
102
+ MUSTACHE_ERR_CLOSURE_MISMATCH,
103
+ MUSTACHE_ERR_FILE_NOT_FOUND,
104
+ MUSTACHE_ERR_FILE_TOO_BIG,
105
+ MUSTACHE_ERR_FILE_NAME_TOO_LONG,
106
+ MUSTACHE_ERR_EMPTY_TEMPLATE,
107
+ MUSTACHE_ERR_UNKNOWN,
108
+ MUSTACHE_ERR_USER_ERROR,
109
+ } mustache_error_en;
110
+
111
+ /** Arguments for the `mustache_load` function. */
112
+ typedef struct {
113
+ /** The root template's file name. */
114
+ char const *filename;
115
+ /** The file name's length. */
116
+ size_t filename_len;
117
+ /** Parsing error reporting (can be NULL). */
118
+ mustache_error_en *err;
119
+ } mustache_load_args_s;
120
+
121
+ /**
122
+ * Allows this header to be included within another header while limiting
123
+ * exposure.
124
+ *
125
+ * before including the header within an implementation faile, define
126
+ * INCLUDE_MUSTACHE_IMPLEMENTATION as 1.
127
+ */
128
+ #if INCLUDE_MUSTACHE_IMPLEMENTATION
129
+
130
+ MUSTACHE_FUNC mustache_s *mustache_load(mustache_load_args_s args);
131
+
132
+ #define mustache_load(...) mustache_load((mustache_load_args_s){__VA_ARGS__})
133
+
134
+ /** free the mustache template */
135
+ inline MUSTACHE_FUNC void mustache_free(mustache_s *mustache) {
136
+ free(mustache);
137
+ }
138
+
139
+ /** Arguments for the `mustache_build` function. */
140
+ typedef struct {
141
+ /** The parsed template (an instruction collection). */
142
+ mustache_s *mustache;
143
+ /** Opaque user data (recommended for input review) - children will inherit
144
+ * the parent's udata value. Updated values will propegate to child sections
145
+ * but won't effect parent sections.
146
+ */
147
+ void *udata1;
148
+ /** Opaque user data (recommended for output handling)- children will inherit
149
+ * the parent's udata value. Updated values will propegate to child sections
150
+ * but won't effect parent sections.
151
+ */
152
+ void *udata2;
153
+ /** Formatting error reporting (can be NULL). */
154
+ mustache_error_en *err;
155
+ } mustache_build_args_s;
156
+ MUSTACHE_FUNC int mustache_build(mustache_build_args_s args);
157
+
158
+ #define mustache_build(mustache_s_ptr, ...) \
159
+ mustache_build( \
160
+ (mustache_build_args_s){.mustache = (mustache_s_ptr), __VA_ARGS__})
161
+
162
+ /* *****************************************************************************
163
+ Client Callbacks - MUST be implemented by the including file
164
+ ***************************************************************************** */
165
+
166
+ /**
167
+ * A mustache section allows the callbacks to "walk" backwards towards the root
168
+ * in search of argument data.
169
+ *
170
+ * Note that every section is allowed a separate udata value.
171
+ */
172
+ typedef struct mustache_section_s {
173
+ /**
174
+ * READ ONLY. The parent section (when nesting), if any.
175
+ *
176
+ * This is important for accessing the parent's `udata` values when searching
177
+ * for an argument's value.
178
+ *
179
+ * The root's parent is NULL.
180
+ */
181
+ struct mustache_section_s *parent;
182
+ /** Opaque user data (recommended for input review) - children will inherit
183
+ * the parent's udata value. Updated values will propegate to child sections
184
+ * but won't effect parent sections.
185
+ */
186
+ void *udata1;
187
+ /** Opaque user data (recommended for output handling)- children will inherit
188
+ * the parent's udata value. Updated values will propegate to child sections
189
+ * but won't effect parent sections.
190
+ */
191
+ void *udata2;
192
+ } mustache_section_s;
193
+
194
+ /**
195
+ * Called when an argument name was detected in the current section.
196
+ *
197
+ * A conforming implementation will search for the named argument both in the
198
+ * existing section and all of it's parents (walking backwards towards the root)
199
+ * until a value is detected.
200
+ *
201
+ * A missing value should be treated the same as an empty string.
202
+ *
203
+ * A conforming implementation will output the named argument's value (either
204
+ * HTML escaped or not, depending on the `escape` flag) as a string.
205
+ */
206
+ static int mustache_on_arg(mustache_section_s *section, const char *name,
207
+ uint32_t name_len, unsigned char escape);
208
+
209
+ /**
210
+ * Called when simple template text (string) is detected.
211
+ *
212
+ * A conforming implementation will output data as a string (no escaping).
213
+ */
214
+ static int mustache_on_text(mustache_section_s *section, const char *data,
215
+ uint32_t data_len);
216
+
217
+ /**
218
+ * Called for nested sections, must return the number of objects in the new
219
+ * subsection (depending on the argument's name).
220
+ *
221
+ * Arrays should return the number of objects in the array.
222
+ *
223
+ * `true` values should return 1.
224
+ *
225
+ * `false` values should return 0.
226
+ *
227
+ * A return value of -1 will stop processing with an error.
228
+ *
229
+ * Please note, this will handle both normal and inverted sections.
230
+ */
231
+ static int32_t mustache_on_section_test(mustache_section_s *section,
232
+ const char *name, uint32_t name_len);
233
+
234
+ /**
235
+ * Called when entering a nested section.
236
+ *
237
+ * `index` is a zero based index indicating the number of repetitions that
238
+ * occurred so far (same as the array index for arrays).
239
+ *
240
+ * A return value of -1 will stop processing with an error.
241
+ *
242
+ * Note: this is a good time to update the subsection's `udata` with the value
243
+ * of the array index. The `udata` will always contain the value or the parent's
244
+ * `udata`.
245
+ */
246
+ static int mustache_on_section_start(mustache_section_s *section,
247
+ char const *name, uint32_t name_len,
248
+ uint32_t index);
249
+
250
+ /**
251
+ * Called for cleanup in case of error.
252
+ */
253
+ static void mustache_on_formatting_error(void *udata1, void *udata2);
254
+
255
+ /* *****************************************************************************
256
+
257
+ IMPLEMENTATION (beware: monolithic functions ahead)
258
+
259
+ ***************************************************************************** */
260
+
261
+ /* *****************************************************************************
262
+ Internal types
263
+ ***************************************************************************** */
264
+
265
+ struct mustache_s {
266
+ /* The number of instructions in the engine */
267
+ union {
268
+ void *read_only_pt; /* ensure pointer wide padding */
269
+ struct {
270
+ uint32_t intruction_count;
271
+ uint32_t data_length;
272
+ } read_only;
273
+ } u;
274
+ };
275
+
276
+ typedef struct mustache__instruction_s {
277
+ enum {
278
+ MUSTACHE_WRITE_TEXT,
279
+ MUSTACHE_WRITE_ARG,
280
+ MUSTACHE_WRITE_ARG_UNESCAPED,
281
+ MUSTACHE_SECTION_START,
282
+ MUSTACHE_SECTION_START_INV,
283
+ MUSTACHE_SECTION_END,
284
+ MUSTACHE_SECTION_GOTO,
285
+ } instruction;
286
+ /** the data the instruction acts upon */
287
+ struct {
288
+ /** The offset from the beginning of the data segment. */
289
+ uint32_t start;
290
+ /** The length of the data. */
291
+ uint32_t len;
292
+ } data;
293
+ } mustache__instruction_s;
294
+
295
+ /* *****************************************************************************
296
+ Calling the instrustion list (using the template engine)
297
+ ***************************************************************************** */
298
+
299
+ /*
300
+ * This function reviews the instructions listed at the end of the mustache_s
301
+ * and performs any callbacks necessary.
302
+ *
303
+ * The `mustache_s` data is looks like this:
304
+ *
305
+ * - header (the `mustache_s` struct): lists the length of the instruction
306
+ * array and data segments.
307
+ * - Instruction array: lists all the instructions extracted from the
308
+ * template(s) (an array of `mustache__instruction_s`).
309
+ * - Data segment: text and data related to the instructions.
310
+ *
311
+ * The instructions, much like machine code, might loop or jump. This is why the
312
+ * functions keep a stack of sorts. This allows the code to avoid recursion and
313
+ * minimize any risk of stack overflow caused by recursive templates.
314
+ *
315
+ * Note:
316
+ *
317
+ * For text and argument instructions, the mustache__instruction_s.data.start
318
+ * and mustache__instruction_s.data.len mark the beginning of the text/argument
319
+ * and it's name.
320
+ *
321
+ * However, for MUSTACHE_SECTION_START instructions, data.len marks position for
322
+ * the complementing MUSTACHE_SECTION_END instruction, allowing for easy jumps
323
+ * in cases where a section is skipped or in cases of a recursive template.
324
+ */
325
+ MUSTACHE_FUNC int(mustache_build)(mustache_build_args_s args) {
326
+ /* extract the instruction array and data segment from the mustache_s */
327
+ mustache__instruction_s *pos =
328
+ (mustache__instruction_s *)(sizeof(*args.mustache) +
329
+ (uintptr_t)args.mustache);
330
+ mustache__instruction_s *const start = pos;
331
+ mustache__instruction_s *const end =
332
+ pos + args.mustache->u.read_only.intruction_count;
333
+ char *const data = (char *const)end;
334
+
335
+ /* prepare a pre-allocated stack space to flatten recursion needs */
336
+ struct {
337
+ mustache_section_s sec; /* client visible section data */
338
+ uint32_t start; /* section start instruction position */
339
+ uint32_t end; /* instruction to jump to after completion */
340
+ uint32_t index; /* zero based index forr section loops */
341
+ uint32_t count; /* the number of times the section should be performed */
342
+ } section_stack[MUSTACHE_NESTING_LIMIT];
343
+
344
+ /* first section (section 0) data */
345
+ section_stack[0].sec = (mustache_section_s){
346
+ .udata1 = args.udata1,
347
+ .udata2 = args.udata2,
348
+ };
349
+ section_stack[0].end = 0;
350
+ uint32_t nesting_pos = 0;
351
+
352
+ /* run through the instruction list and persorm each instruction */
353
+ while (pos < end) {
354
+ switch (pos->instruction) {
355
+
356
+ case MUSTACHE_WRITE_TEXT:
357
+ if (mustache_on_text(&section_stack[nesting_pos].sec,
358
+ data + pos->data.start, pos->data.len) == -1) {
359
+ if (args.err) {
360
+ *args.err = MUSTACHE_ERR_USER_ERROR;
361
+ }
362
+ goto error;
363
+ }
364
+ break;
365
+
366
+ case MUSTACHE_WRITE_ARG:
367
+ if (mustache_on_arg(&section_stack[nesting_pos].sec,
368
+ data + pos->data.start, pos->data.len, 1) == -1) {
369
+ if (args.err) {
370
+ *args.err = MUSTACHE_ERR_USER_ERROR;
371
+ }
372
+ goto error;
373
+ }
374
+ break;
375
+
376
+ case MUSTACHE_WRITE_ARG_UNESCAPED:
377
+ if (mustache_on_arg(&section_stack[nesting_pos].sec,
378
+ data + pos->data.start, pos->data.len, 0) == -1) {
379
+ if (args.err) {
380
+ *args.err = MUSTACHE_ERR_USER_ERROR;
381
+ }
382
+ goto error;
383
+ }
384
+ break;
385
+ case MUSTACHE_SECTION_START_INV: /* overfloaw*/
386
+ case MUSTACHE_SECTION_START: {
387
+ /* starting a new section, increased nesting & review */
388
+ if (nesting_pos + 1 == MUSTACHE_NESTING_LIMIT) {
389
+ if (args.err) {
390
+ *args.err = MUSTACHE_ERR_TOO_DEEP;
391
+ }
392
+ goto error;
393
+ }
394
+ section_stack[nesting_pos + 1].sec = section_stack[nesting_pos].sec;
395
+ ++nesting_pos;
396
+
397
+ /* find the end of the section */
398
+ mustache__instruction_s *section_end = start + pos->data.len;
399
+
400
+ /* test for template (partial) section (nameless) */
401
+ if (pos->data.start == 0) {
402
+ section_stack[nesting_pos].end = section_end - start;
403
+ section_stack[nesting_pos].start = pos - start;
404
+ section_stack[nesting_pos].index = 1;
405
+ section_stack[nesting_pos].count = 0;
406
+ break;
407
+ }
408
+
409
+ /* test for user abort signal and cycle value */
410
+ int32_t val = mustache_on_section_test(&section_stack[nesting_pos].sec,
411
+ data + pos->data.start,
412
+ strlen(data + pos->data.start));
413
+ if (val == -1) {
414
+ if (args.err) {
415
+ *args.err = MUSTACHE_ERR_USER_ERROR;
416
+ }
417
+ goto error;
418
+ }
419
+ if (pos->instruction == MUSTACHE_SECTION_START_INV) {
420
+ if (val == 0) {
421
+ /* perform once for inverted sections */
422
+ val = 1;
423
+ } else {
424
+ /* or don't perform */
425
+ val = 0;
426
+ }
427
+ }
428
+
429
+ if (val == 0) {
430
+ --nesting_pos;
431
+ pos = section_end;
432
+ } else {
433
+ /* save start/end positions and index counter */
434
+ section_stack[nesting_pos].end = section_end - start;
435
+ section_stack[nesting_pos].start = pos - start;
436
+ section_stack[nesting_pos].index = val;
437
+ section_stack[nesting_pos].count = 0;
438
+ if (mustache_on_section_start(&section_stack[nesting_pos].sec,
439
+ data + pos->data.start,
440
+ strlen(data + pos->data.start),
441
+ section_stack[nesting_pos].count) == -1) {
442
+ if (args.err) {
443
+ *args.err = MUSTACHE_ERR_USER_ERROR;
444
+ }
445
+ goto error;
446
+ }
447
+ }
448
+ break;
449
+ }
450
+
451
+ case MUSTACHE_SECTION_END:
452
+ ++section_stack[nesting_pos].count;
453
+ if (section_stack[nesting_pos].index > section_stack[nesting_pos].count) {
454
+ pos = start + section_stack[nesting_pos].start;
455
+ if (nesting_pos) { /* revert to old udata values */
456
+ section_stack[nesting_pos].sec = section_stack[nesting_pos - 1].sec;
457
+ }
458
+ if (mustache_on_section_start(&section_stack[nesting_pos].sec,
459
+ data + pos->data.start,
460
+ strlen(data + pos->data.start),
461
+ section_stack[nesting_pos].count)) {
462
+ if (args.err) {
463
+ *args.err = MUSTACHE_ERR_USER_ERROR;
464
+ }
465
+ goto error;
466
+ }
467
+ } else {
468
+ pos = start + section_stack[nesting_pos].end; /* in case of recursion */
469
+ --nesting_pos;
470
+ }
471
+ break;
472
+
473
+ case MUSTACHE_SECTION_GOTO: {
474
+ /* used to handle recursive sections and re-occuring partials. */
475
+ if (nesting_pos + 1 == MUSTACHE_NESTING_LIMIT) {
476
+ if (args.err) {
477
+ *args.err = MUSTACHE_ERR_TOO_DEEP;
478
+ }
479
+ goto error;
480
+ }
481
+ section_stack[nesting_pos + 1].sec = section_stack[nesting_pos].sec;
482
+ ++nesting_pos;
483
+ if (start[pos->data.len].data.start == 0) {
484
+ section_stack[nesting_pos].end = pos - start;
485
+ section_stack[nesting_pos].index = 1;
486
+ section_stack[nesting_pos].count = 0;
487
+ section_stack[nesting_pos].start = pos->data.len;
488
+ pos = start + pos->data.len;
489
+ break;
490
+ }
491
+ int32_t val = mustache_on_section_test(
492
+ &section_stack[nesting_pos].sec,
493
+ data + start[pos->data.len].data.start,
494
+ strlen(data + start[pos->data.len].data.start));
495
+ if (val == -1) {
496
+ if (args.err) {
497
+ *args.err = MUSTACHE_ERR_USER_ERROR;
498
+ }
499
+ goto error;
500
+ }
501
+ if (val == 0) {
502
+ --nesting_pos;
503
+ } else {
504
+ /* save start/end positions and index counter */
505
+ section_stack[nesting_pos].end = pos - start;
506
+ section_stack[nesting_pos].index = val;
507
+ section_stack[nesting_pos].count = 0;
508
+ section_stack[nesting_pos].start = pos->data.len;
509
+ pos = start + pos->data.len;
510
+ if (mustache_on_section_start(&section_stack[nesting_pos].sec,
511
+ data + pos->data.start,
512
+ strlen(data + pos->data.start),
513
+ section_stack[nesting_pos].count) == -1) {
514
+ if (args.err) {
515
+ *args.err = MUSTACHE_ERR_USER_ERROR;
516
+ }
517
+ goto error;
518
+ }
519
+ }
520
+ } break;
521
+ default:
522
+ /* not a valid engine */
523
+ fprintf(stderr, "ERROR: invalid mustache instruction set detected (wrong "
524
+ "`mustache_s`?)\n");
525
+ if (args.err) {
526
+ *args.err = MUSTACHE_ERR_UNKNOWN;
527
+ }
528
+ goto error;
529
+ }
530
+ ++pos;
531
+ }
532
+
533
+ return 0;
534
+ error:
535
+ mustache_on_formatting_error(args.udata1, args.udata2);
536
+ return -1;
537
+ }
538
+
539
+ /* *****************************************************************************
540
+ Building the instrustion list (parsing the template)
541
+ ***************************************************************************** */
542
+
543
+ /* The parsing implementation, converts a template to an instruction array */
544
+ MUSTACHE_FUNC mustache_s *(mustache_load)(mustache_load_args_s args) {
545
+ /* Make sure the args string length is set and prepare the path name */
546
+ char *path = NULL;
547
+ uint32_t path_capa = 0;
548
+ uint32_t path_len = 0;
549
+ if (args.filename && !args.filename_len) {
550
+ args.filename_len = strlen(args.filename);
551
+ }
552
+
553
+ /* copy the path data (and resolve) into writable memory */
554
+ if (args.filename[0] == '~' && args.filename[1] == '/' && getenv("HOME")) {
555
+ const char *home = getenv("HOME");
556
+ path_len = strlen(home);
557
+ path_capa =
558
+ path_len + 1 + args.filename_len + 1 + 9 + 1; /* + file extension */
559
+ path = malloc(path_capa);
560
+ if (!path) {
561
+ perror("FATAL ERROR: couldn't allocate memory for path resolution");
562
+ exit(errno);
563
+ }
564
+ memcpy(path, home, path_len);
565
+ if (path[path_len - 1] != '/') {
566
+ path[path_len++] = '/';
567
+ }
568
+ memcpy(path + path_len, args.filename + 2, args.filename_len);
569
+ args.filename_len += path_len;
570
+ args.filename = path;
571
+ }
572
+ /* divide faile name from the root path to the file */
573
+
574
+ /*
575
+ * We need a dynamic array to hold the list of instructions...
576
+ * We might as well use the same memory structure as the final product, saving
577
+ * us an allocation and a copy at the end.
578
+ *
579
+ * Allocation starts with 32 instructions.
580
+ */
581
+ struct {
582
+ mustache_s head; /* instruction array capacity and length */
583
+ mustache__instruction_s ary[]; /* the instruction array */
584
+ } *instructions =
585
+ malloc(sizeof(*instructions) + (32 * sizeof(mustache__instruction_s)));
586
+ if (!instructions) {
587
+ perror(
588
+ "FATAL ERROR: couldn't allocate memory for mustache template parsing");
589
+ exit(errno);
590
+ }
591
+ /* initialize dynamic array */
592
+ instructions->head.u.read_only.intruction_count = 0;
593
+ instructions->head.u.read_only.data_length = 32;
594
+ uint32_t data_len = 0;
595
+ uint8_t *data = NULL;
596
+
597
+ /* We define a dynamic array handling macro, using 32 instruction chunks */
598
+ #define PUSH_INSTRUCTION(...) \
599
+ do { \
600
+ if (instructions->head.u.read_only.intruction_count == \
601
+ instructions->head.u.read_only.data_length) { \
602
+ instructions->head.u.read_only.data_length += 32; \
603
+ instructions = realloc(instructions, \
604
+ sizeof(*instructions) + \
605
+ (instructions->head.u.read_only.data_length * \
606
+ sizeof(mustache__instruction_s))); \
607
+ if (!instructions) { \
608
+ perror("FATAL ERROR: couldn't reallocate memory for mustache " \
609
+ "template path"); \
610
+ exit(errno); \
611
+ } \
612
+ } \
613
+ instructions->ary[instructions->head.u.read_only.intruction_count++] = \
614
+ (mustache__instruction_s){__VA_ARGS__}; \
615
+ } while (0);
616
+
617
+ /* a limited local template stack to manage template data "jumps" */
618
+ /* Note: templates can be recursive. */
619
+ int32_t stack_pos = 0;
620
+ struct {
621
+ uint8_t *delimiter_start; /* currunt instruction start delimiter */
622
+ uint8_t *delimiter_end; /* currunt instruction end delimiter */
623
+ uint32_t data_start; /* template starting position (with header) */
624
+ uint32_t data_pos; /* data reading position (how much was consumed) */
625
+ uint32_t data_end; /* data ending position (for this template) */
626
+ uint16_t del_start_len; /* delimiter length (start) */
627
+ uint16_t del_end_len; /* delimiter length (end) */
628
+ } template_stack[MUSTACHE_NESTING_LIMIT];
629
+ template_stack[0].data_start = 0;
630
+ template_stack[0].data_pos = 0;
631
+ template_stack[0].data_end = 0;
632
+ template_stack[0].delimiter_start = (uint8_t *)"{{";
633
+ template_stack[0].delimiter_end = (uint8_t *)"}}";
634
+ template_stack[0].del_start_len = 2;
635
+ template_stack[0].del_end_len = 2;
636
+
637
+ /* a section data stack, allowing us to safely mark section closures */
638
+ int32_t section_depth = 0;
639
+ struct {
640
+ /* section name, for closure validation */
641
+ struct {
642
+ uint32_t start;
643
+ uint32_t len;
644
+ } name;
645
+ /* position for the section start instruction */
646
+ uint32_t instruction_pos;
647
+ } section_stack[MUSTACHE_NESTING_LIMIT];
648
+
649
+ #define SECTION2FILENAME() (data + template_stack[stack_pos].data_start + 10)
650
+ #define SECTION2FLEN() \
651
+ ((((uint8_t *)data + template_stack[stack_pos].data_start + 4)[0] << 1) | \
652
+ (((uint8_t *)data + template_stack[stack_pos].data_start + 4)[1]))
653
+
654
+ /* append a filename to the path, managing the C string memory and length */
655
+ #define PATH2FULL(folder, folder_len, filename, filename_len) \
656
+ do { \
657
+ if (path_capa < (filename_len) + (folder_len) + 9 + 1) { \
658
+ path_capa = (filename_len) + (folder_len) + 9 + 1; \
659
+ path = realloc(path, path_capa); \
660
+ if (!path) { \
661
+ perror("FATAL ERROR: couldn't allocate memory for path resolution"); \
662
+ exit(errno); \
663
+ } \
664
+ } \
665
+ if ((folder_len) && (filename)[0] != '/') { \
666
+ memcpy(path, (folder), (folder_len)); \
667
+ path_len = (folder_len); \
668
+ } else { \
669
+ path_len = 0; \
670
+ } \
671
+ if (path != (char *)(filename)) \
672
+ memcpy(path + path_len, (filename), (filename_len)); \
673
+ path_len += (filename_len); \
674
+ path[path_len] = 0; \
675
+ } while (0);
676
+
677
+ /* append a filename to the path, managing the C string memory and length */
678
+ #define PATH_WITH_EXT() \
679
+ do { \
680
+ memcpy(path + path_len, ".mustache", 9); \
681
+ path[path_len + 9] = 0; /* keep path_len the same */ \
682
+ } while (0);
683
+
684
+ /* We define a dynamic template loading macro to manage memory details */
685
+ #define LOAD_TEMPLATE(root, root_len, filename, filname_len) \
686
+ do { \
687
+ /* find root filename's path start */ \
688
+ int32_t root_len_tmp = (root_len); \
689
+ while (root_len_tmp && (((char *)(root))[root_len_tmp - 1] != '/' || \
690
+ (root_len_tmp > 1 && \
691
+ ((char *)(root))[root_len_tmp - 2] == '\\'))) { \
692
+ --root_len_tmp; \
693
+ } \
694
+ if ((filname_len) + root_len_tmp >= ((uint32_t)1 << 16)) { \
695
+ *args.err = MUSTACHE_ERR_FILE_NAME_TOO_LONG; \
696
+ goto error; \
697
+ } \
698
+ PATH2FULL((root), root_len_tmp, (filename), (filname_len)); \
699
+ struct stat f_data; \
700
+ { \
701
+ /* test file name with and without the .mustache extension */ \
702
+ int stat_result = stat(path, &f_data); \
703
+ if (stat_result == -1) { \
704
+ PATH_WITH_EXT(); \
705
+ stat_result = stat(path, &f_data); \
706
+ } \
707
+ if (stat_result == -1) { \
708
+ if (args.err) { \
709
+ *args.err = MUSTACHE_ERR_FILE_NOT_FOUND; \
710
+ } \
711
+ goto error; \
712
+ } \
713
+ } \
714
+ if (f_data.st_size >= ((uint32_t)1 << 24)) { \
715
+ *args.err = MUSTACHE_ERR_FILE_TOO_BIG; \
716
+ goto error; \
717
+ } \
718
+ /* the data segment's new length after loading the the template */ \
719
+ /* The data segments includes a template header: */ \
720
+ /* | 4 bytes template start instruction position | */ \
721
+ /* | 2 bytes template name | 4 bytes next template position | */ \
722
+ /* | template name (filename) | ...[template data]... */ \
723
+ /* this allows template data to be reused when repeating a template */ \
724
+ const uint32_t new_len = \
725
+ data_len + 4 + 2 + 4 + path_len + f_data.st_size + 1; \
726
+ /* reallocate memory */ \
727
+ data = realloc(data, new_len); \
728
+ if (!data) { \
729
+ perror("FATAL ERROR: couldn't reallocate memory for mustache " \
730
+ "data segment"); \
731
+ exit(errno); \
732
+ } \
733
+ /* save instruction position length into template header */ \
734
+ data[data_len + 0] = \
735
+ (instructions->head.u.read_only.intruction_count >> 3) & 0xFF; \
736
+ data[data_len + 1] = \
737
+ (instructions->head.u.read_only.intruction_count >> 2) & 0xFF; \
738
+ data[data_len + 2] = \
739
+ (instructions->head.u.read_only.intruction_count >> 1) & 0xFF; \
740
+ data[data_len + 3] = \
741
+ (instructions->head.u.read_only.intruction_count) & 0xFF; \
742
+ /* Add section start marker (to support recursion or repeated partials) */ \
743
+ PUSH_INSTRUCTION(.instruction = MUSTACHE_SECTION_START); \
744
+ /* save filename length */ \
745
+ data[data_len + 4 + 0] = (path_len >> 1) & 0xFF; \
746
+ data[data_len + 4 + 1] = path_len & 0xFF; \
747
+ /* save data length ("next" pointer) */ \
748
+ data[data_len + 4 + 2 + 0] = ((uint32_t)new_len >> 3) & 0xFF; \
749
+ data[data_len + 4 + 2 + 1] = ((uint32_t)new_len >> 2) & 0xFF; \
750
+ data[data_len + 4 + 2 + 2] = ((uint32_t)new_len >> 1) & 0xFF; \
751
+ data[data_len + 4 + 2 + 3] = ((uint32_t)new_len) & 0xFF; \
752
+ /* copy filename */ \
753
+ memcpy(data + data_len + 4 + 2 + 4, path, path_len); \
754
+ /* open file and dump it into the data segment after the new header */ \
755
+ int fd = open(path, O_RDONLY); \
756
+ if (fd == -1) { \
757
+ if (args.err) { \
758
+ *args.err = MUSTACHE_ERR_FILE_NOT_FOUND; \
759
+ } \
760
+ goto error; \
761
+ } \
762
+ if (pread(fd, (data + data_len + 4 + 3 + 3 + path_len), f_data.st_size, \
763
+ 0) != (ssize_t)f_data.st_size) { \
764
+ if (args.err) { \
765
+ *args.err = MUSTACHE_ERR_FILE_NOT_FOUND; \
766
+ } \
767
+ close(fd); \
768
+ goto error; \
769
+ } \
770
+ if (stack_pos + 1 == MUSTACHE_NESTING_LIMIT) { \
771
+ if (args.err) { \
772
+ *args.err = MUSTACHE_ERR_TOO_DEEP; \
773
+ } \
774
+ close(fd); \
775
+ goto error; \
776
+ } \
777
+ close(fd); \
778
+ /* increase the data stack pointer and setup new stack frame */ \
779
+ ++stack_pos; \
780
+ template_stack[stack_pos].data_start = data_len; \
781
+ template_stack[stack_pos].data_pos = data_len + 4 + 3 + 3 + path_len; \
782
+ template_stack[stack_pos].data_end = new_len - 1; \
783
+ template_stack[stack_pos].delimiter_start = (uint8_t *)"{{"; \
784
+ template_stack[stack_pos].delimiter_end = (uint8_t *)"}}"; \
785
+ template_stack[stack_pos].del_start_len = 2; \
786
+ template_stack[stack_pos].del_end_len = 2; \
787
+ /* update new data segment length and add NUL marker */ \
788
+ data_len = new_len; \
789
+ data[new_len - 1] = 0; \
790
+ } while (0);
791
+
792
+ #define IGNORE_WHITESPACE(str, step) \
793
+ while (isspace(*(str))) { \
794
+ (str) += (step); \
795
+ }
796
+
797
+ /* Our first template to load is the root template */
798
+ LOAD_TEMPLATE(NULL, 0, args.filename, args.filename_len);
799
+
800
+ /*** As long as the stack has templated to parse - parse the template ***/
801
+ while (stack_pos) {
802
+ /* test reading position against template ending and parse */
803
+ while (template_stack[stack_pos].data_pos <
804
+ template_stack[stack_pos].data_end) {
805
+ /* start parsing at current position */
806
+ uint8_t *const start = data + template_stack[stack_pos].data_pos;
807
+ /* find the next instruction (beg == beginning) */
808
+ uint8_t *beg = (uint8_t *)strstr(
809
+ (char *)start, (char *)template_stack[stack_pos].delimiter_start);
810
+ if (!beg || beg >= data + template_stack[stack_pos].data_end) {
811
+ /* no instructions left, only text */
812
+ PUSH_INSTRUCTION(.instruction = MUSTACHE_WRITE_TEXT,
813
+ .data = {
814
+ .start = template_stack[stack_pos].data_pos,
815
+ .len = template_stack[stack_pos].data_end -
816
+ template_stack[stack_pos].data_pos,
817
+ });
818
+ template_stack[stack_pos].data_pos = template_stack[stack_pos].data_end;
819
+ continue;
820
+ }
821
+ if (beg + template_stack[stack_pos].del_start_len >=
822
+ data + template_stack[stack_pos].data_end) {
823
+ /* overshot... ending the template with a delimiter...*/
824
+ if (args.err) {
825
+ *args.err = MUSTACHE_ERR_CLOSURE_MISMATCH;
826
+ }
827
+ goto error;
828
+ }
829
+ beg[0] = 0; /* mark the end of any text segment or string, just in case */
830
+ /* find the ending of the instruction */
831
+ uint8_t *end = (uint8_t *)strstr(
832
+ (char *)beg + template_stack[stack_pos].del_start_len,
833
+ (char *)template_stack[stack_pos].delimiter_end);
834
+ if (!end || end >= data + template_stack[stack_pos].data_end) {
835
+ /* delimiter not closed */
836
+ if (args.err) {
837
+ *args.err = MUSTACHE_ERR_CLOSURE_MISMATCH;
838
+ }
839
+ goto error;
840
+ }
841
+ /* text before instruction? add text instruction */
842
+ if (beg != data + template_stack[stack_pos].data_pos) {
843
+ PUSH_INSTRUCTION(.instruction = MUSTACHE_WRITE_TEXT,
844
+ .data = {
845
+ .start = template_stack[stack_pos].data_pos,
846
+ .len = beg -
847
+ (data + template_stack[stack_pos].data_pos),
848
+ });
849
+ }
850
+ /* update reading position in the stack */
851
+ template_stack[stack_pos].data_pos =
852
+ (end - data) + template_stack[stack_pos].del_end_len;
853
+
854
+ /* move the beginning marker the the instruction's content */
855
+ beg += template_stack[stack_pos].del_start_len;
856
+
857
+ /* review template instruction (the {{tag}}) */
858
+ uint8_t escape_str = 1;
859
+ switch (beg[0]) {
860
+ case '!':
861
+ /* comment, do nothing */
862
+ break;
863
+
864
+ case '=':
865
+ /* define new seperators */
866
+ ++beg;
867
+ --end;
868
+ if (end[0] != '=') {
869
+ if (args.err) {
870
+ *args.err = MUSTACHE_ERR_CLOSURE_MISMATCH;
871
+ }
872
+ goto error;
873
+ }
874
+ {
875
+ uint8_t *div = beg;
876
+ while (div < end && !isspace(*(char *)div)) {
877
+ ++div;
878
+ }
879
+ if (div == end) {
880
+ if (args.err) {
881
+ *args.err = MUSTACHE_ERR_CLOSURE_MISMATCH;
882
+ }
883
+ goto error;
884
+ }
885
+ template_stack[stack_pos].delimiter_start = beg;
886
+ template_stack[stack_pos].del_start_len = div - beg;
887
+ div[0] = 0;
888
+ ++div;
889
+ IGNORE_WHITESPACE(div, 1);
890
+ template_stack[stack_pos].delimiter_end = div;
891
+ template_stack[stack_pos].del_end_len = end - div;
892
+ end[0] = 0;
893
+ }
894
+ break;
895
+
896
+ case '^': /*overflow*/
897
+ escape_str = 0;
898
+ case '#':
899
+ /* start section (or inverted section) */
900
+ ++beg;
901
+ --end;
902
+ IGNORE_WHITESPACE(beg, 1);
903
+ IGNORE_WHITESPACE(end, -1);
904
+ end[1] = 0;
905
+ if (section_depth >= MUSTACHE_NESTING_LIMIT) {
906
+ if (args.err) {
907
+ *args.err = MUSTACHE_ERR_CLOSURE_MISMATCH;
908
+ }
909
+ goto error;
910
+ }
911
+ section_stack[section_depth].instruction_pos =
912
+ instructions->head.u.read_only.intruction_count;
913
+ section_stack[section_depth].name.start = beg - data;
914
+ section_stack[section_depth].name.len = (end - beg) + 1;
915
+ ++section_depth;
916
+ PUSH_INSTRUCTION(.instruction =
917
+ (escape_str ? MUSTACHE_SECTION_START
918
+ : MUSTACHE_SECTION_START_INV),
919
+ .data = {
920
+ .start = beg - data,
921
+ .len = (end - beg) + 1,
922
+ });
923
+ break;
924
+
925
+ case '>':
926
+ /* partial template - search data for loaded template or load new */
927
+ ++beg;
928
+ --end;
929
+ IGNORE_WHITESPACE(beg, 1);
930
+ IGNORE_WHITESPACE(end, -1);
931
+ ++end;
932
+ end[0] = 0;
933
+ {
934
+ uint8_t *loaded = data;
935
+ uint8_t *const data_end = data + data_len;
936
+ while (loaded < data_end) {
937
+ uint32_t const fn_len =
938
+ ((loaded[4] & 0xFF) << 1) | (loaded[5] & 0xFF);
939
+ if (fn_len != end - beg || memcmp(beg, loaded + 10, end - beg)) {
940
+ uint32_t const next_offset =
941
+ ((loaded[6] & 0xFF) << 3) | ((loaded[7] & 0xFF) << 2) |
942
+ ((loaded[8] & 0xFF) << 1) | (loaded[9] & 0xFF);
943
+ loaded = data + next_offset;
944
+ continue;
945
+ }
946
+ uint32_t const section_start =
947
+ ((loaded[0] & 0xFF) << 3) | ((loaded[1] & 0xFF) << 2) |
948
+ ((loaded[2] & 0xFF) << 1) | (loaded[3] & 0xFF);
949
+ PUSH_INSTRUCTION(.instruction = MUSTACHE_SECTION_GOTO,
950
+ .data = {
951
+ .len = section_start,
952
+ });
953
+ break;
954
+ }
955
+ if (loaded >= data_end) {
956
+ LOAD_TEMPLATE(SECTION2FILENAME(), SECTION2FLEN(), beg, (end - beg));
957
+ }
958
+ }
959
+ break;
960
+
961
+ case '/':
962
+ /* section end */
963
+ ++beg;
964
+ --end;
965
+ IGNORE_WHITESPACE(beg, 1);
966
+ IGNORE_WHITESPACE(end, -1);
967
+ end[1] = 0;
968
+ --section_depth;
969
+ if (!(section_depth + 1) ||
970
+ (end - beg) + 1 != section_stack[section_depth].name.len ||
971
+ memcmp(beg, data + section_stack[section_depth].name.start,
972
+ section_stack[section_depth].name.len)) {
973
+ if (args.err) {
974
+ *args.err = MUSTACHE_ERR_CLOSURE_MISMATCH;
975
+ }
976
+ goto error;
977
+ }
978
+ /* update the section_start instruction with the ending's location */
979
+ instructions->ary[section_stack[section_depth].instruction_pos]
980
+ .data.len = instructions->head.u.read_only.intruction_count;
981
+ /* push sction end instruction */
982
+ PUSH_INSTRUCTION(.instruction = MUSTACHE_SECTION_END,
983
+ .data = {
984
+ .len =
985
+ section_stack[section_depth].instruction_pos,
986
+ });
987
+ break;
988
+
989
+ case '{':
990
+ /* step the read position forward if the ending was '}}}' */
991
+ if ((data + template_stack[stack_pos].data_pos)[0] == '}') {
992
+ ++template_stack[stack_pos].data_pos;
993
+ }
994
+ /*overflow*/
995
+ case '&': /*overflow*/
996
+ /* unescaped variable data */
997
+ escape_str = 0;
998
+ /* overflow to default */
999
+ case ':': /*overflow*/
1000
+ case '<': /*overflow*/
1001
+ ++beg; /*overflow*/
1002
+ default:
1003
+ --end;
1004
+ IGNORE_WHITESPACE(beg, 1);
1005
+ IGNORE_WHITESPACE(end, -1);
1006
+ end[1] = 0;
1007
+ PUSH_INSTRUCTION(.instruction =
1008
+ (escape_str ? MUSTACHE_WRITE_ARG
1009
+ : MUSTACHE_WRITE_ARG_UNESCAPED),
1010
+ .data = {
1011
+ .start = beg - data,
1012
+ .len = (end - beg) + 1,
1013
+ });
1014
+ break;
1015
+ }
1016
+ }
1017
+ /* templates are treated as sections, allowing for recursion using "goto" */
1018
+ /* update the template's section_start instruction with the end position */
1019
+ uint32_t const section_start =
1020
+ ((data[template_stack[stack_pos].data_start + 0] & 0xFF) << 3) |
1021
+ ((data[template_stack[stack_pos].data_start + 1] & 0xFF) << 2) |
1022
+ ((data[template_stack[stack_pos].data_start + 2] & 0xFF) << 1) |
1023
+ (data[template_stack[stack_pos].data_start + 3] & 0xFF);
1024
+ instructions->ary[section_start].data.len =
1025
+ instructions->head.u.read_only.intruction_count;
1026
+ /* add section end instructiomn for the template section */
1027
+ PUSH_INSTRUCTION(.instruction = MUSTACHE_SECTION_END,
1028
+ .data.len = section_start);
1029
+ /* pop the stack frame */
1030
+ --stack_pos;
1031
+ }
1032
+ /*** done parsing ***/
1033
+
1034
+ /* is the template empty?*/
1035
+ if (!instructions->head.u.read_only.intruction_count) {
1036
+ if (args.err) {
1037
+ *args.err = MUSTACHE_ERR_EMPTY_TEMPLATE;
1038
+ }
1039
+ goto error;
1040
+ }
1041
+
1042
+ /* We're done making up the instruction list, time to finalize the product */
1043
+ /* Make room for the String data at the end of the instruction array */
1044
+ instructions = realloc(instructions,
1045
+ sizeof(*instructions) +
1046
+ (instructions->head.u.read_only.intruction_count *
1047
+ sizeof(mustache__instruction_s)) +
1048
+ data_len);
1049
+ if (!instructions) {
1050
+ perror("FATAL ERROR: couldn't reallocate memory for mustache "
1051
+ "template finalization");
1052
+ exit(errno);
1053
+ }
1054
+ /* Copy the data segment to the end of the instruction array */
1055
+ instructions->head.u.read_only.data_length = data_len;
1056
+ memcpy((void *)((uintptr_t)(instructions + 1) +
1057
+ (instructions->head.u.read_only.intruction_count *
1058
+ sizeof(mustache__instruction_s))),
1059
+ data, data_len);
1060
+ /* Cleanup, set error code and return. */
1061
+ free(data);
1062
+ free(path);
1063
+ if (args.err) {
1064
+ *args.err = MUSTACHE_OK;
1065
+ }
1066
+ return &instructions->head;
1067
+ error:
1068
+ free(instructions);
1069
+ free(data);
1070
+ free(path);
1071
+ return NULL;
1072
+
1073
+ #undef PATH2FULL
1074
+ #undef PATH_WITH_EXT
1075
+ #undef SECTION2FILENAME
1076
+ #undef SECTION2FLEN
1077
+ #undef LOAD_TEMPLATE
1078
+ #undef PUSH_INSTRUCTION
1079
+ #undef IGNORE_WHITESPACE
1080
+ }
1081
+
1082
+ #endif /* INCLUDE_MUSTACHE_IMPLEMENTATION */
1083
+
1084
+ #undef MUSTACHE_FUNC
1085
+ #endif /* H_MUSTACHE_LOADR_H */