iodine 0.7.12 → 0.7.13

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.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 754d32e0aca1e86482fbe961e45127e068c9890d3cff09c67e70349872bfa87e
4
- data.tar.gz: e7d9c1abacfec11b27eeb8879585f12c010ddb52c64938cffe530d60f325dd69
3
+ metadata.gz: 943a41860e82f16cade6bb4f0ba8eabab9d712eb620de04cfbeca7153e4e8551
4
+ data.tar.gz: ef24434b749862566b869ff248089bc5f420a46b0492644096a40588766d3c3e
5
5
  SHA512:
6
- metadata.gz: f6141afd0ec6d66f0de2cfb0d85d997be21287b0c307c1f370374f5ed2758c77aa206a4e9a09abd7a7ee701423177655e5c67ef3dec69de3a189b3597177c258
7
- data.tar.gz: 6bc527391894adeb5431d2fa57e6c8c073036775ad4cd4f4571765f9baa3e01cdb4eb00b39c230d7296d8df707bdffc0f926ef3a8bf66c3d0c21eed28132ea5c
6
+ metadata.gz: 95c9c98a8b85201a98989e96379fbbaeb74e3d16ae9ea4b42bf3fd49d7591d7224bb406d568298b19583411f675b337110d464ad7447221ee19583d10e6a37aa
7
+ data.tar.gz: 4614ca29b759b2c1c8e6329c38b565415eb69c356298a53429f0af100545fe1944e1340ccf6334e55d444d8f7659c13c3e6d1f45e6b9d384972acbaa59f7f8d4
@@ -6,6 +6,12 @@ Please notice that this change log contains changes for upcoming releases as wel
6
6
 
7
7
  ## Changes:
8
8
 
9
+ #### Change log v.0.7.13
10
+
11
+ **Fix**: (`mustache`) added support for padding in template partials.
12
+
13
+ **Fix**: (`mustache`) added support for method names as keys (i.e., `{{user.to_json}}` or `{{#user}}{{to_json}}{{/user}}`). Note: no arguments may be passed (no Ruby code parsing, just testing against method names).
14
+
9
15
  #### Change log v.0.7.12
10
16
 
11
17
  **Fix**: (`mustache`) fixed multiple issues with `Iodine::Mustache` and added lambda support for mustache templates.
@@ -6448,10 +6448,15 @@ static size_t fio_mem_block_count;
6448
6448
  "(fio) Total memory blocks allocated before cleanup %zu\n" \
6449
6449
  " Maximum memory blocks allocated at a single time %zu\n", \
6450
6450
  fio_mem_block_count, fio_mem_block_count_max)
6451
+ #define FIO_MEMORY_PRINT_BLOCK_STAT_END() \
6452
+ FIO_LOG_INFO("(fio) Total memory blocks allocated " \
6453
+ "after cleanup (possible leak) %zu\n", \
6454
+ fio_mem_block_count)
6451
6455
  #else
6452
6456
  #define FIO_MEMORY_ON_BLOCK_ALLOC()
6453
6457
  #define FIO_MEMORY_ON_BLOCK_FREE()
6454
6458
  #define FIO_MEMORY_PRINT_BLOCK_STAT()
6459
+ #define FIO_MEMORY_PRINT_BLOCK_STAT_END()
6455
6460
  #endif
6456
6461
  /* *****************************************************************************
6457
6462
  Per-CPU Arena management
@@ -6722,7 +6727,7 @@ static void fio_mem_destroy(void) {
6722
6727
  if (!memory.forked && fio_ls_embd_any(&memory.available)) {
6723
6728
  FIO_LOG_WARNING("facil.io detected memory traces remaining after cleanup"
6724
6729
  " - memory leak?");
6725
- FIO_MEMORY_PRINT_BLOCK_STAT();
6730
+ FIO_MEMORY_PRINT_BLOCK_STAT_END();
6726
6731
  size_t count = 0;
6727
6732
  FIO_LS_EMBD_FOR(&memory.available, node) { ++count; }
6728
6733
  FIO_LOG_DEBUG("Memory pool size: %zu (%zu blocks per allocation).", count,
@@ -73,71 +73,6 @@ FIOBJ fiobj_mustache_build(mustache_s *mustache, FIOBJ data) {
73
73
  Mustache Callbacks
74
74
  ***************************************************************************** */
75
75
 
76
- /** HTML ecape table, created using the following Ruby Script:
77
-
78
- a = (0..255).to_a.map {|i| i.chr }
79
- # 100.times {|i| a[i] = "&\#x#{ i < 16 ? "0#{i.to_s(16)}" : i.to_s(16)};"}
80
- 100.times {|i| a[i] = "&\##{i.to_s(10)};"}
81
- ('a'.ord..'z'.ord).each {|i| a[i] = i.chr }
82
- ('A'.ord..'Z'.ord).each {|i| a[i] = i.chr }
83
- ('0'.ord..'9'.ord).each {|i| a[i] = i.chr }
84
- a['<'.ord] = "&lt;"
85
- a['>'.ord] = "&gt;"
86
- a['&'.ord] = "&amp;"
87
- a['"'.ord] = "&quot;"
88
- a["\'".ord] = "&apos;"
89
- a['|'.ord] = "&\##{'|'.ord.to_s(10)};"
90
-
91
- b = a.map {|s| s.length }
92
- puts "static char *html_escape_strs[] = {", a.to_s.slice(1..-2) ,"};",
93
- "static uint8_t html_escape_len[] = {", b.to_s.slice(1..-2),"};"
94
- */
95
- static char *html_escape_strs[] = {
96
- "&#0;", "&#1;", "&#2;", "&#3;", "&#4;", "&#5;", "&#6;", "&#7;",
97
- "&#8;", "&#9;", "&#10;", "&#11;", "&#12;", "&#13;", "&#14;", "&#15;",
98
- "&#16;", "&#17;", "&#18;", "&#19;", "&#20;", "&#21;", "&#22;", "&#23;",
99
- "&#24;", "&#25;", "&#26;", "&#27;", "&#28;", "&#29;", "&#30;", "&#31;",
100
- "&#32;", "&#33;", "&quot;", "&#35;", "&#36;", "&#37;", "&amp;", "&apos;",
101
- "&#40;", "&#41;", "&#42;", "&#43;", "&#44;", "&#45;", "&#46;", "&#47;",
102
- "0", "1", "2", "3", "4", "5", "6", "7",
103
- "8", "9", "&#58;", "&#59;", "&lt;", "&#61;", "&gt;", "&#63;",
104
- "&#64;", "A", "B", "C", "D", "E", "F", "G",
105
- "H", "I", "J", "K", "L", "M", "N", "O",
106
- "P", "Q", "R", "S", "T", "U", "V", "W",
107
- "X", "Y", "Z", "&#91;", "&#92;", "&#93;", "&#94;", "&#95;",
108
- "&#96;", "a", "b", "c", "d", "e", "f", "g",
109
- "h", "i", "j", "k", "l", "m", "n", "o",
110
- "p", "q", "r", "s", "t", "u", "v", "w",
111
- "x", "y", "z", "{", "&#124;", "}", "~", "\x7F",
112
- "\x80", "\x81", "\x82", "\x83", "\x84", "\x85", "\x86", "\x87",
113
- "\x88", "\x89", "\x8A", "\x8B", "\x8C", "\x8D", "\x8E", "\x8F",
114
- "\x90", "\x91", "\x92", "\x93", "\x94", "\x95", "\x96", "\x97",
115
- "\x98", "\x99", "\x9A", "\x9B", "\x9C", "\x9D", "\x9E", "\x9F",
116
- "\xA0", "\xA1", "\xA2", "\xA3", "\xA4", "\xA5", "\xA6", "\xA7",
117
- "\xA8", "\xA9", "\xAA", "\xAB", "\xAC", "\xAD", "\xAE", "\xAF",
118
- "\xB0", "\xB1", "\xB2", "\xB3", "\xB4", "\xB5", "\xB6", "\xB7",
119
- "\xB8", "\xB9", "\xBA", "\xBB", "\xBC", "\xBD", "\xBE", "\xBF",
120
- "\xC0", "\xC1", "\xC2", "\xC3", "\xC4", "\xC5", "\xC6", "\xC7",
121
- "\xC8", "\xC9", "\xCA", "\xCB", "\xCC", "\xCD", "\xCE", "\xCF",
122
- "\xD0", "\xD1", "\xD2", "\xD3", "\xD4", "\xD5", "\xD6", "\xD7",
123
- "\xD8", "\xD9", "\xDA", "\xDB", "\xDC", "\xDD", "\xDE", "\xDF",
124
- "\xE0", "\xE1", "\xE2", "\xE3", "\xE4", "\xE5", "\xE6", "\xE7",
125
- "\xE8", "\xE9", "\xEA", "\xEB", "\xEC", "\xED", "\xEE", "\xEF",
126
- "\xF0", "\xF1", "\xF2", "\xF3", "\xF4", "\xF5", "\xF6", "\xF7",
127
- "\xF8", "\xF9", "\xFA", "\xFB", "\xFC", "\xFD", "\xFE", "\xFF"};
128
- static uint8_t html_escape_len[] = {
129
- 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
130
- 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 5, 5, 5, 5, 6, 5, 5, 5, 5, 5, 5, 5, 5,
131
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 5, 4, 5, 4, 5, 5, 1, 1, 1, 1, 1, 1, 1,
132
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 5, 5, 5, 5,
133
- 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
134
- 1, 1, 1, 1, 6, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
135
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
136
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
137
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
138
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
139
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
140
-
141
76
  static inline FIOBJ fiobj_mustache_find_obj(mustache_section_s *section,
142
77
  const char *name,
143
78
  uint32_t name_len) {
@@ -169,30 +104,10 @@ static int mustache_on_arg(mustache_section_s *section, const char *name,
169
104
  FIOBJ o = fiobj_mustache_find_obj(section, name, name_len);
170
105
  if (!o)
171
106
  return 0;
172
- if (!escape || !FIOBJ_TYPE_IS(o, FIOBJ_T_STRING)) {
173
- fiobj_str_join((FIOBJ)section->udata1, o);
174
- return 0;
175
- }
176
- /* TODO: html escape */
177
- fio_str_info_s str = fiobj_obj2cstr(o);
178
- if (!str.len)
179
- return 0;
180
107
  fio_str_info_s i = fiobj_obj2cstr(o);
181
- i.capa = fiobj_str_capa_assert((FIOBJ)section->udata1, i.len + str.len + 64);
182
- do {
183
- if (i.len + 6 >= i.capa)
184
- i.capa = fiobj_str_capa_assert((FIOBJ)section->udata1, i.capa + 64);
185
- i.len = fiobj_str_write((FIOBJ)section->udata1,
186
- html_escape_strs[(uint8_t)str.data[0]],
187
- html_escape_len[(uint8_t)str.data[0]]);
188
- --str.len;
189
- ++str.data;
190
- } while (str.len);
191
- (void)section;
192
- (void)name;
193
- (void)name_len;
194
- (void)escape;
195
- return 0;
108
+ if (!i.len)
109
+ return 0;
110
+ return mustache_write_text(section, i.data, i.len, escape);
196
111
  }
197
112
 
198
113
  /**
@@ -230,7 +145,7 @@ static int32_t mustache_on_section_test(mustache_section_s *section,
230
145
  if (FIOBJ_TYPE_IS(o, FIOBJ_T_ARRAY))
231
146
  return fiobj_ary_count(o);
232
147
  return 1;
233
- (void)callable;
148
+ (void)callable; /* FIOBJ doesn't support lambdas */
234
149
  }
235
150
 
236
151
  /**
@@ -122,6 +122,11 @@ static inline VALUE fiobj_mustache_find_obj_absolute(VALUE udata,
122
122
  if (!RB_TYPE_P(udata, T_HASH)) {
123
123
  if (name_len == 1 && name[0] == '.')
124
124
  return udata;
125
+ /* search by method */
126
+ ID name_id = rb_intern2(name, name_len);
127
+ if (rb_respond_to(udata, name_id)) {
128
+ return IodineCaller.call(udata, name_id);
129
+ }
125
130
  return Qnil;
126
131
  }
127
132
  /* search by String */
@@ -130,8 +135,14 @@ static inline VALUE fiobj_mustache_find_obj_absolute(VALUE udata,
130
135
  if (tmp != Qnil)
131
136
  return tmp;
132
137
  /* search by Symbol */
133
- key = rb_id2sym(rb_intern2(name, name_len));
138
+ ID name_id = rb_intern2(name, name_len);
139
+ key = rb_id2sym(name_id);
134
140
  tmp = rb_hash_aref(udata, key);
141
+ /* search by method */
142
+ if (tmp == Qnil && rb_respond_to(udata, name_id)) {
143
+ tmp = IodineCaller.call(udata, name_id);
144
+ }
145
+
135
146
  return tmp;
136
147
  }
137
148
 
@@ -175,10 +186,11 @@ static inline VALUE fiobj_mustache_find_obj(mustache_section_s *section,
175
186
  dot = 0;
176
187
  while (dot < name_len && name[dot] != '.')
177
188
  ++dot;
178
- if (dot == name_len)
189
+ if (dot == name_len) {
179
190
  return Qnil;
191
+ }
180
192
  tmp = fiobj_mustache_find_obj_absolute(tmp, name, dot);
181
- if (tmp == Qnil || !RB_TYPE_P(tmp, T_HASH))
193
+ if (tmp == Qnil)
182
194
  return Qnil;
183
195
  ++dot;
184
196
  }
@@ -206,39 +218,15 @@ static int mustache_on_arg(mustache_section_s *section, const char *name,
206
218
  fio_str_write(section->udata1, "true", 4);
207
219
  break;
208
220
  }
209
- if (rb_respond_to(o, call_func_id))
210
- goto callable;
211
- if (!RB_TYPE_P(o, T_STRING))
212
- o = IodineCaller.call(o, to_s_func_id);
221
+ if (!RB_TYPE_P(o, T_STRING)) {
222
+ if (rb_respond_to(o, call_func_id))
223
+ o = IodineCaller.call(o, call_func_id);
224
+ if (!RB_TYPE_P(o, T_STRING))
225
+ o = IodineCaller.call(o, to_s_func_id);
226
+ }
213
227
  if (!RB_TYPE_P(o, T_STRING) || !RSTRING_LEN(o))
214
228
  return 0;
215
- if (!escape) {
216
- fio_str_write(section->udata1, RSTRING_PTR(o), RSTRING_LEN(o));
217
- return 0;
218
- }
219
- /* HTML escape */
220
- fio_str_info_s str = {.data = RSTRING_PTR(o), .len = RSTRING_LEN(o)};
221
- fio_str_info_s i = fio_str_capa_assert(
222
- section->udata1, fio_str_len(section->udata1) + str.len + 64);
223
- do {
224
- if (i.len + 6 >= i.capa)
225
- i = fio_str_capa_assert(section->udata1, i.capa + 64);
226
- i = fio_str_write(section->udata1, html_escape_strs[(uint8_t)str.data[0]],
227
- html_escape_len[(uint8_t)str.data[0]]);
228
- --str.len;
229
- ++str.data;
230
- } while (str.len);
231
- (void)section;
232
- (void)name;
233
- (void)name_len;
234
- (void)escape;
235
- return 0;
236
- callable:
237
- o = IodineCaller.call(o, call_func_id);
238
- if (RB_TYPE_P(o, T_STRING))
239
- o = rb_funcall2(o, to_s_func_id, 0, NULL);
240
- fio_str_write(section->udata1, RSTRING_PTR(o), RSTRING_LEN(o));
241
- return 0;
229
+ return mustache_write_text(section, RSTRING_PTR(o), RSTRING_LEN(o), escape);
242
230
  }
243
231
 
244
232
  /**
@@ -287,7 +275,7 @@ static int32_t mustache_on_section_test(mustache_section_s *section,
287
275
  if (!RB_TYPE_P(o, T_STRING))
288
276
  o = rb_funcall2(o, to_s_func_id, 0, NULL);
289
277
  if (RB_TYPE_P(o, T_STRING) && RSTRING_LEN(o))
290
- fio_str_write(section->udata1, RSTRING_PTR(o), RSTRING_LEN(o));
278
+ mustache_write_text(section, RSTRING_PTR(o), RSTRING_LEN(o), 0);
291
279
  return 0;
292
280
  }
293
281
  return 1;
@@ -359,10 +347,10 @@ error:
359
347
  "Iodine::Mustache template error, closure mismatch.");
360
348
  break;
361
349
  case MUSTACHE_ERR_FILE_NOT_FOUND:
362
- rb_raise(rb_eRuntimeError, "Iodine::Mustache template not found.");
350
+ rb_raise(rb_eLoadError, "Iodine::Mustache template not found.");
363
351
  break;
364
352
  case MUSTACHE_ERR_FILE_TOO_BIG:
365
- rb_raise(rb_eRuntimeError, "Iodine::Mustache template too big.");
353
+ rb_raise(rb_eLoadError, "Iodine::Mustache template too big.");
366
354
  break;
367
355
  case MUSTACHE_ERR_FILE_NAME_TOO_LONG:
368
356
  rb_raise(rb_eRuntimeError, "Iodine::Mustache template name too long.");
@@ -528,10 +516,10 @@ error:
528
516
  "Iodine::Mustache template error, closure mismatch.");
529
517
  break;
530
518
  case MUSTACHE_ERR_FILE_NOT_FOUND:
531
- rb_raise(rb_eRuntimeError, "Iodine::Mustache template not found.");
519
+ rb_raise(rb_eLoadError, "Iodine::Mustache template not found.");
532
520
  break;
533
521
  case MUSTACHE_ERR_FILE_TOO_BIG:
534
- rb_raise(rb_eRuntimeError, "Iodine::Mustache template too big.");
522
+ rb_raise(rb_eLoadError, "Iodine::Mustache template too big.");
535
523
  break;
536
524
  case MUSTACHE_ERR_FILE_NAME_TOO_LONG:
537
525
  rb_raise(rb_eRuntimeError, "Iodine::Mustache template name too long.");
@@ -71,7 +71,7 @@ Feel free to copy, use and enjoy according to the license provided.
71
71
 
72
72
  #if !defined(MUSTACHE_NESTING_LIMIT) || !MUSTACHE_NESTING_LIMIT
73
73
  #undef MUSTACHE_NESTING_LIMIT
74
- #define MUSTACHE_NESTING_LIMIT 96
74
+ #define MUSTACHE_NESTING_LIMIT 82
75
75
  #endif
76
76
 
77
77
  #if !defined(__GNUC__) && !defined(__clang__) && !defined(FIO_GNUC_BYPASS)
@@ -89,7 +89,7 @@ Feel free to copy, use and enjoy according to the license provided.
89
89
  #endif
90
90
 
91
91
  /* *****************************************************************************
92
- Mustache API Functions and Arguments
92
+ Mustache API Argument types
93
93
  ***************************************************************************** */
94
94
 
95
95
  /** an opaque type for mustache template data (when caching). */
@@ -125,15 +125,27 @@ typedef struct {
125
125
  mustache_error_en *err;
126
126
  } mustache_load_args_s;
127
127
 
128
+ /* *****************************************************************************
129
+ REQUIRED: Define INCLUDE_MUSTACHE_IMPLEMENTATION only in the implementation file
130
+ ***************************************************************************** */
131
+
128
132
  /**
129
- * Allows this header to be included within another header while limiting
130
- * exposure.
133
+ * In non-implementation files, don't define the INCLUDE_MUSTACHE_IMPLEMENTATION
134
+ * macro.
131
135
  *
132
- * before including the header within an implementation faile, define
136
+ * Before including the header within an implementation faile, define
133
137
  * INCLUDE_MUSTACHE_IMPLEMENTATION as 1.
138
+ *
139
+ * When unset (or zero), this will expose the return error reporting and
140
+ * function argument types, allowing them to be exposed in a public facing API.
141
+ * exposure to the function return and argument types.
134
142
  */
135
143
  #if INCLUDE_MUSTACHE_IMPLEMENTATION
136
144
 
145
+ /* *****************************************************************************
146
+ Mustache API Functions and Arguments
147
+ ***************************************************************************** */
148
+
137
149
  MUSTACHE_FUNC mustache_s *mustache_load(mustache_load_args_s args);
138
150
 
139
151
  #define mustache_load(...) mustache_load((mustache_load_args_s){__VA_ARGS__})
@@ -203,6 +215,18 @@ Callbacks Helpers - These functions can be called from within callbacks
203
215
  static inline mustache_section_s *
204
216
  mustache_section_parent(mustache_section_s *section);
205
217
 
218
+ /**
219
+ * This helper function should be used to write text to the output
220
+ * stream form within `mustache_on_arg` or `mustache_on_section_test`.
221
+ *
222
+ * This function will call the `mustache_on_text` callback for each slice of
223
+ * text that requires padding and for escaped data.
224
+ *
225
+ * `mustache_on_text` must NEVER call this function.
226
+ */
227
+ static inline int mustache_write_text(mustache_section_s *section, char *text,
228
+ uint32_t len, uint8_t escape);
229
+
206
230
  /**
207
231
  * Returns the section's unparsed content as a non-NUL terminated byte array.
208
232
  *
@@ -328,6 +352,9 @@ typedef struct mustache__instruction_s {
328
352
  MUSTACHE_SECTION_START_INV,
329
353
  MUSTACHE_SECTION_END,
330
354
  MUSTACHE_SECTION_GOTO,
355
+ MUSTACHE_PADDING_PUSH,
356
+ MUSTACHE_PADDING_POP,
357
+ MUSTACHE_PADDING_WRITE,
331
358
  } instruction;
332
359
  /** the data the instruction acts upon */
333
360
  struct {
@@ -353,15 +380,24 @@ typedef struct {
353
380
  uint16_t frame; /* the stack frame's index (zero based) */
354
381
  } mustache__section_stack_frame_s;
355
382
 
383
+ /*
384
+ * The stack memory is placed in a structure to allow stack unrolling and
385
+ * avoiding recursion with it's stack overflow risks.
386
+ */
356
387
  typedef struct {
357
388
  mustache_s *data; /* the mustache template being built */
358
389
  uint32_t pos; /* the instruction postision index */
390
+ uint32_t padding; /* padding instruction position */
359
391
  uint16_t index; /* the stack postision index */
360
392
  mustache__section_stack_frame_s stack[MUSTACHE_NESTING_LIMIT];
361
393
  } mustache__builder_stack_s;
362
394
 
363
- #define MUSTACHE_DELIMITER_LENGTH_LIMIT 11
395
+ #define MUSTACHE_DELIMITER_LENGTH_LIMIT 5
364
396
 
397
+ /*
398
+ * The stack memory is placed in a structure to allow stack unrolling and
399
+ * avoiding recursion with it's stack overflow risks.
400
+ */
365
401
  typedef struct {
366
402
  mustache_s *m;
367
403
  mustache__instruction_s *i;
@@ -370,6 +406,7 @@ typedef struct {
370
406
  char *path;
371
407
  uint32_t i_capa;
372
408
  uint32_t data_len;
409
+ uint32_t padding;
373
410
  uint16_t path_len;
374
411
  uint16_t path_capa;
375
412
  uint16_t index; /* stack index */
@@ -409,6 +446,18 @@ Callbacks Helper Implementation
409
446
  (str) += (step); \
410
447
  }
411
448
 
449
+ /*
450
+ * used internally by some of the helpers to find convert a section pointer to
451
+ * the full builder stack data.
452
+ */
453
+ static inline mustache__builder_stack_s *
454
+ mustache___section2stack(mustache_section_s *section) {
455
+ mustache__section_stack_frame_s *f =
456
+ (mustache__section_stack_frame_s *)section;
457
+ return MUSTACHE_OBJECT_OFFSET(mustache__builder_stack_s, stack,
458
+ (f - f->frame));
459
+ }
460
+
412
461
  /**
413
462
  * Returns the section's parent for nested sections, or NULL (for root section).
414
463
  *
@@ -442,10 +491,7 @@ static inline const char *mustache_section_text(mustache_section_s *section,
442
491
  size_t *p_len) {
443
492
  if (!section || !p_len)
444
493
  goto error;
445
- mustache__section_stack_frame_s *f =
446
- (mustache__section_stack_frame_s *)section;
447
- mustache__builder_stack_s *s =
448
- MUSTACHE_OBJECT_OFFSET(mustache__builder_stack_s, stack, (f - f->frame));
494
+ mustache__builder_stack_s *s = mustache___section2stack(section);
449
495
  mustache__instruction_s *inst =
450
496
  MUSTACH2INSTRUCTIONS(s->data) + s->pos; /* current instruction*/
451
497
  if (inst->instruction != MUSTACHE_SECTION_START)
@@ -459,6 +505,148 @@ error:
459
505
  return NULL;
460
506
  }
461
507
 
508
+ /**
509
+ * used internally to write escaped text rather than clear text.
510
+ */
511
+ static inline int mustache__write_padding(mustache__builder_stack_s *s) {
512
+ mustache__instruction_s *const inst = MUSTACH2INSTRUCTIONS(s->data);
513
+ char *const data = MUSTACH2DATA(s->data);
514
+ for (uint32_t i = s->padding; i; i = inst[i].data.end) {
515
+ if (mustache_on_text(&s->stack[s->index].sec, data + inst[i].data.name_pos,
516
+ inst[i].data.name_len) == -1)
517
+ return -1;
518
+ }
519
+ return 0;
520
+ }
521
+
522
+ /**
523
+ * used internally to write escaped text rather than clear text.
524
+ */
525
+ static int mustache__write_escaped(mustache__builder_stack_s *s, char *text,
526
+ uint32_t len) {
527
+ /** HTML ecape table, created using the following Ruby Script:
528
+
529
+ a = (0..255).to_a.map {|i| i.chr }
530
+ # 100.times {|i| a[i] = "&\#x#{ i < 16 ? "0#{i.to_s(16)}" : i.to_s(16)};"}
531
+ 100.times {|i| a[i] = "&\##{i.to_s(10)};"}
532
+ ('a'.ord..'z'.ord).each {|i| a[i] = i.chr }
533
+ ('A'.ord..'Z'.ord).each {|i| a[i] = i.chr }
534
+ ('0'.ord..'9'.ord).each {|i| a[i] = i.chr }
535
+ a['<'.ord] = "&lt;"
536
+ a['>'.ord] = "&gt;"
537
+ a['&'.ord] = "&amp;"
538
+ a['"'.ord] = "&quot;"
539
+ a["\'".ord] = "&apos;"
540
+ a['|'.ord] = "&\##{'|'.ord.to_s(10)};"
541
+
542
+ b = a.map {|s| s.length }
543
+ puts "static char *html_escape_strs[] = {", a.to_s.slice(1..-2) ,"};",
544
+ "static uint8_t html_escape_len[] = {", b.to_s.slice(1..-2),"};"
545
+ */
546
+ static char *html_escape_strs[] = {
547
+ "&#0;", "&#1;", "&#2;", "&#3;", "&#4;", "&#5;", "&#6;", "&#7;",
548
+ "&#8;", "&#9;", "&#10;", "&#11;", "&#12;", "&#13;", "&#14;", "&#15;",
549
+ "&#16;", "&#17;", "&#18;", "&#19;", "&#20;", "&#21;", "&#22;", "&#23;",
550
+ "&#24;", "&#25;", "&#26;", "&#27;", "&#28;", "&#29;", "&#30;", "&#31;",
551
+ "&#32;", "&#33;", "&quot;", "&#35;", "&#36;", "&#37;", "&amp;", "&apos;",
552
+ "&#40;", "&#41;", "&#42;", "&#43;", "&#44;", "&#45;", "&#46;", "&#47;",
553
+ "0", "1", "2", "3", "4", "5", "6", "7",
554
+ "8", "9", "&#58;", "&#59;", "&lt;", "&#61;", "&gt;", "&#63;",
555
+ "&#64;", "A", "B", "C", "D", "E", "F", "G",
556
+ "H", "I", "J", "K", "L", "M", "N", "O",
557
+ "P", "Q", "R", "S", "T", "U", "V", "W",
558
+ "X", "Y", "Z", "&#91;", "&#92;", "&#93;", "&#94;", "&#95;",
559
+ "&#96;", "a", "b", "c", "d", "e", "f", "g",
560
+ "h", "i", "j", "k", "l", "m", "n", "o",
561
+ "p", "q", "r", "s", "t", "u", "v", "w",
562
+ "x", "y", "z", "{", "&#124;", "}", "~", "\x7F",
563
+ "\x80", "\x81", "\x82", "\x83", "\x84", "\x85", "\x86", "\x87",
564
+ "\x88", "\x89", "\x8A", "\x8B", "\x8C", "\x8D", "\x8E", "\x8F",
565
+ "\x90", "\x91", "\x92", "\x93", "\x94", "\x95", "\x96", "\x97",
566
+ "\x98", "\x99", "\x9A", "\x9B", "\x9C", "\x9D", "\x9E", "\x9F",
567
+ "\xA0", "\xA1", "\xA2", "\xA3", "\xA4", "\xA5", "\xA6", "\xA7",
568
+ "\xA8", "\xA9", "\xAA", "\xAB", "\xAC", "\xAD", "\xAE", "\xAF",
569
+ "\xB0", "\xB1", "\xB2", "\xB3", "\xB4", "\xB5", "\xB6", "\xB7",
570
+ "\xB8", "\xB9", "\xBA", "\xBB", "\xBC", "\xBD", "\xBE", "\xBF",
571
+ "\xC0", "\xC1", "\xC2", "\xC3", "\xC4", "\xC5", "\xC6", "\xC7",
572
+ "\xC8", "\xC9", "\xCA", "\xCB", "\xCC", "\xCD", "\xCE", "\xCF",
573
+ "\xD0", "\xD1", "\xD2", "\xD3", "\xD4", "\xD5", "\xD6", "\xD7",
574
+ "\xD8", "\xD9", "\xDA", "\xDB", "\xDC", "\xDD", "\xDE", "\xDF",
575
+ "\xE0", "\xE1", "\xE2", "\xE3", "\xE4", "\xE5", "\xE6", "\xE7",
576
+ "\xE8", "\xE9", "\xEA", "\xEB", "\xEC", "\xED", "\xEE", "\xEF",
577
+ "\xF0", "\xF1", "\xF2", "\xF3", "\xF4", "\xF5", "\xF6", "\xF7",
578
+ "\xF8", "\xF9", "\xFA", "\xFB", "\xFC", "\xFD", "\xFE", "\xFF"};
579
+ static uint8_t html_escape_len[] = {
580
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
581
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 5, 5, 5, 5, 6, 5, 5, 5, 5, 5, 5, 5, 5,
582
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 5, 4, 5, 4, 5, 5, 1, 1, 1, 1, 1, 1, 1,
583
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 5, 5, 5, 5,
584
+ 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
585
+ 1, 1, 1, 1, 6, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
586
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
587
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
588
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
589
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
590
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
591
+ char buffer[4096];
592
+ size_t pos = 0;
593
+ const char *end = text + len;
594
+ while (text < end) {
595
+ if (*text == '\n' && s->padding) {
596
+ buffer[pos++] = '\n';
597
+ buffer[pos] = 0;
598
+ if (mustache_on_text(&s->stack[s->index].sec, buffer, pos) == -1)
599
+ return -1;
600
+ pos = 0;
601
+ if (mustache__write_padding(s) == -1)
602
+ return -1;
603
+ } else {
604
+ memcpy(buffer + pos, html_escape_strs[(uint8_t)text[0]],
605
+ html_escape_len[(uint8_t)text[0]]);
606
+ pos += html_escape_len[(uint8_t)text[0]];
607
+ if (pos >= 4090) {
608
+ buffer[pos] = 0;
609
+ if (mustache_on_text(&s->stack[s->index].sec, buffer, pos) == -1)
610
+ return -1;
611
+ pos = 0;
612
+ }
613
+ }
614
+ ++text;
615
+ }
616
+ if (pos) {
617
+ buffer[pos] = 0;
618
+ if (mustache_on_text(&s->stack[s->index].sec, buffer, pos) == -1)
619
+ return -1;
620
+ }
621
+ return 0;
622
+ }
623
+ /**
624
+ * This helper function should be used to write text to the output
625
+ * stream (often used within the `mustache_on_arg` callback).
626
+ */
627
+ static inline int mustache_write_text(mustache_section_s *section, char *text,
628
+ uint32_t len, uint8_t escape) {
629
+ mustache__builder_stack_s *s = mustache___section2stack(section);
630
+ if (escape)
631
+ return mustache__write_escaped(s, text, len);
632
+ /* TODO */
633
+ char *end = memchr(text, '\n', len);
634
+ while (len && end) {
635
+ ++end;
636
+ const uint32_t slice_len = end - text;
637
+ if (mustache_on_text(&s->stack[s->index].sec, text, slice_len) == -1)
638
+ return -1;
639
+ text = end;
640
+ len -= slice_len;
641
+ end = memchr(text, '\n', len);
642
+ if (mustache__write_padding(s) == -1)
643
+ return -1;
644
+ }
645
+ if (len && mustache_on_text(&s->stack[s->index].sec, text, len) == -1)
646
+ return -1;
647
+ return 0;
648
+ }
649
+
462
650
  /* *****************************************************************************
463
651
  Internal Helpers
464
652
  ***************************************************************************** */
@@ -517,6 +705,26 @@ mustache__data_segment_read(uint8_t *data) {
517
705
  return s;
518
706
  }
519
707
 
708
+ static inline void mustache__stand_alone_adjust(mustache__loader_stack_s *s,
709
+ uint32_t stand_alone) {
710
+ if (!stand_alone)
711
+ return;
712
+ /* adjust reading position */
713
+ s->stack[s->index].data_pos +=
714
+ 1 + (s->data[s->stack[s->index].data_pos] == '\r');
715
+ /* remove any padding from the beginning of the line */
716
+ if (s->m->u.read_only.intruction_count &&
717
+ s->i[s->m->u.read_only.intruction_count - 1].instruction ==
718
+ MUSTACHE_WRITE_TEXT) {
719
+ mustache__instruction_s *ins =
720
+ s->i + s->m->u.read_only.intruction_count - 1;
721
+ if (ins->data.name_len <= (stand_alone >> 1))
722
+ --s->m->u.read_only.intruction_count;
723
+ else
724
+ ins->data.name_len -= (stand_alone >> 1);
725
+ }
726
+ }
727
+
520
728
  /* pushes an instruction to the instruction array */
521
729
  static inline int mustache__instruction_push(mustache__loader_stack_s *s,
522
730
  mustache__instruction_s inst) {
@@ -536,6 +744,52 @@ instructions_too_long:
536
744
  return -1;
537
745
  }
538
746
 
747
+ /* pushes text and padding instruction to the instruction array */
748
+ static inline int mustache__push_text_instruction(mustache__loader_stack_s *s,
749
+ uint32_t pos, uint32_t len) {
750
+ /* always push padding instructions, in case or recursion with padding */
751
+ // if (!s->padding) {
752
+ // /* no padding, push text, as is */
753
+ // return mustache__instruction_push(
754
+ // s, (mustache__instruction_s){
755
+ // .instruction = MUSTACHE_WRITE_TEXT,
756
+ // .data = {.name_pos = pos, .name_len = len},
757
+ // });
758
+ // }
759
+ /* insert padding instruction after each new line */
760
+ for (;;) {
761
+ /* seek new line markers */
762
+ char *start = s->data + pos;
763
+ char *end = memchr(s->data + pos, '\n', len);
764
+ if (!end)
765
+ break;
766
+ /* offset marks the line's length */
767
+ const size_t offset = (end - start) + 1;
768
+ /* push text and padding instructions */
769
+ if (mustache__instruction_push(
770
+ s,
771
+ (mustache__instruction_s){
772
+ .instruction = MUSTACHE_WRITE_TEXT,
773
+ .data = {.name_pos = pos, .name_len = offset},
774
+ }) == -1 ||
775
+ mustache__instruction_push(s, (mustache__instruction_s){
776
+ .instruction = MUSTACHE_PADDING_WRITE,
777
+ }) == -1)
778
+ return -1;
779
+ pos += offset;
780
+ len -= offset;
781
+ }
782
+ /* done? */
783
+ if (!len)
784
+ return 0;
785
+ /* write any text that doesn't terminate in the EOL marker */
786
+ return mustache__instruction_push(
787
+ s, (mustache__instruction_s){
788
+ .instruction = MUSTACHE_WRITE_TEXT,
789
+ .data = {.name_pos = pos, .name_len = len},
790
+ });
791
+ }
792
+
539
793
  /*
540
794
  * Returns the instruction's position if the template is already existing.
541
795
  *
@@ -554,9 +808,9 @@ static inline uint32_t mustache__file_is_loaded(mustache__loader_stack_s *s,
554
808
  return (uint32_t)-1;
555
809
  }
556
810
 
557
- static inline int mustache__load_data(mustache__loader_stack_s *s,
558
- const char *name, size_t name_len,
559
- const char *data, size_t data_len) {
811
+ static inline ssize_t mustache__load_data(mustache__loader_stack_s *s,
812
+ const char *name, size_t name_len,
813
+ const char *data, size_t data_len) {
560
814
  const size_t old_len = s->data_len;
561
815
  if (old_len + data_len > UINT32_MAX)
562
816
  goto too_long;
@@ -608,14 +862,14 @@ static inline int mustache__load_data(mustache__loader_stack_s *s,
608
862
  s->stack[s->index].del_end[0] = s->stack[s->index].del_end[1] = '}';
609
863
  s->stack[s->index].del_end[2] = 0;
610
864
  s->stack[s->index].open_sections = 0;
611
- return 0;
865
+ return data_len;
612
866
  too_long:
613
867
  *s->err = MUSTACHE_ERR_TOO_DEEP;
614
868
  return -1;
615
869
  }
616
870
 
617
- static inline int mustache__load_file(mustache__loader_stack_s *s,
618
- const char *name, size_t name_len) {
871
+ static inline ssize_t mustache__load_file(mustache__loader_stack_s *s,
872
+ const char *name, size_t name_len) {
619
873
  struct stat f_data;
620
874
  uint16_t i = s->index;
621
875
  uint32_t old_path_len = 0;
@@ -697,6 +951,9 @@ static inline int mustache__load_file(mustache__loader_stack_s *s,
697
951
  file_found:
698
952
  if (f_data.st_size >= INT32_MAX) {
699
953
  goto file_too_big;
954
+ } else if (f_data.st_size == 0) {
955
+ /* empty, do nothing */
956
+ return 0;
700
957
  } else {
701
958
  /* test if the file was previously loaded */
702
959
  uint32_t pre_existing = mustache__file_is_loaded(s, s->path, old_path_len);
@@ -715,7 +972,7 @@ file_found:
715
972
  return 0;
716
973
  }
717
974
  }
718
- if (mustache__load_data(s, s->path, old_path_len, NULL, f_data.st_size))
975
+ if (mustache__load_data(s, s->path, old_path_len, NULL, f_data.st_size) == -1)
719
976
  goto unknown_error;
720
977
  int fd = open(s->path, O_RDONLY);
721
978
  if (fd == -1)
@@ -724,7 +981,7 @@ file_found:
724
981
  f_data.st_size)
725
982
  goto file_err;
726
983
  close(fd);
727
- return 0;
984
+ return f_data.st_size;
728
985
 
729
986
  name_missing_error:
730
987
  *s->err = MUSTACHE_ERR_FILE_NAME_TOO_SHORT;
@@ -780,6 +1037,7 @@ MUSTACHE_FUNC int(mustache_build)(mustache_build_args_s args) {
780
1037
  s.data = args.mustache;
781
1038
  s.pos = 0;
782
1039
  s.index = 0;
1040
+ s.padding = 0;
783
1041
  s.stack[0] = (mustache__section_stack_frame_s){
784
1042
  .sec =
785
1043
  {
@@ -863,12 +1121,29 @@ MUSTACHE_FUNC int(mustache_build)(mustache_build_args_s args) {
863
1121
  instructions[s.pos].data.name_len,
864
1122
  s.stack[s.index].index) == -1)
865
1123
  goto user_error;
1124
+ /* skip padding instructions in GOTO tags (recursive partials) */
1125
+ if (instructions[s.pos].instruction == MUSTACHE_SECTION_GOTO)
1126
+ ++s.pos;
866
1127
  ++s.stack[s.index].index;
867
1128
  break;
868
1129
  }
869
1130
  s.pos = s.stack[s.index].end;
870
1131
  --s.index;
871
1132
  break;
1133
+ case MUSTACHE_PADDING_PUSH:
1134
+ s.padding = s.pos;
1135
+ break;
1136
+ case MUSTACHE_PADDING_POP:
1137
+ s.padding = instructions[s.padding].data.end;
1138
+ break;
1139
+ case MUSTACHE_PADDING_WRITE:
1140
+ for (uint32_t i = s.padding; i; i = instructions[i].data.end) {
1141
+ if (mustache_on_text(&s.stack[s.index].sec,
1142
+ data + instructions[i].data.name_pos,
1143
+ instructions[i].data.name_len))
1144
+ goto user_error;
1145
+ }
1146
+ break;
872
1147
  default:
873
1148
  /* not a valid engine */
874
1149
  fprintf(stderr, "ERROR: invalid mustache instruction set detected (wrong "
@@ -897,6 +1172,7 @@ Building the instrustion list (parsing the template)
897
1172
  MUSTACHE_FUNC mustache_s *(mustache_load)(mustache_load_args_s args) {
898
1173
  mustache_error_en err_if_missing;
899
1174
  mustache__loader_stack_s s;
1175
+ uint8_t flag = 0;
900
1176
 
901
1177
  if (!args.err)
902
1178
  args.err = &err_if_missing;
@@ -907,6 +1183,7 @@ MUSTACHE_FUNC mustache_s *(mustache_load)(mustache_load_args_s args) {
907
1183
  s.i = NULL;
908
1184
  s.i_capa = 32;
909
1185
  s.index = 0;
1186
+ s.padding = 0;
910
1187
  s.m = malloc(sizeof(*s.m) + (sizeof(*s.i) * 32));
911
1188
  MUSTACHE_ASSERT(s.m, "failed to allocate memory for mustache data");
912
1189
  s.m->u.read_only_pt = 0;
@@ -920,11 +1197,11 @@ MUSTACHE_FUNC mustache_s *(mustache_load)(mustache_load_args_s args) {
920
1197
 
921
1198
  if (args.data) {
922
1199
  if (mustache__load_data(&s, args.filename, args.filename_len, args.data,
923
- args.data_len)) {
1200
+ args.data_len) == -1) {
924
1201
  goto error;
925
1202
  }
926
1203
  } else {
927
- if (mustache__load_file(&s, args.filename, args.filename_len)) {
1204
+ if (mustache__load_file(&s, args.filename, args.filename_len) == -1) {
928
1205
  goto error;
929
1206
  }
930
1207
  }
@@ -933,6 +1210,9 @@ MUSTACHE_FUNC mustache_s *(mustache_load)(mustache_load_args_s args) {
933
1210
  while (s.index) {
934
1211
  /* parsing loop */
935
1212
  while (s.stack[s.index].data_pos < s.stack[s.index].data_end) {
1213
+ /* stand-alone tag flag, also containes padding length after bit 1 */
1214
+ uint32_t stand_alone = 0;
1215
+ uint32_t stand_alone_pos = 0;
936
1216
  /* start parsing at current position */
937
1217
  const char *start = s.data + s.stack[s.index].data_pos;
938
1218
  /* find the next instruction (beg == beginning) */
@@ -940,30 +1220,16 @@ MUSTACHE_FUNC mustache_s *(mustache_load)(mustache_load_args_s args) {
940
1220
  const char *org_beg = beg;
941
1221
  if (!beg || beg >= s.data + s.stack[s.index].data_end) {
942
1222
  /* no instructions left, only text */
943
- mustache__instruction_push(
944
- &s, (mustache__instruction_s){
945
- .instruction = MUSTACHE_WRITE_TEXT,
946
- .data =
947
- {
948
- .name_pos = s.stack[s.index].data_pos,
949
- .name_len = s.stack[s.index].data_end -
950
- s.stack[s.index].data_pos,
951
- },
952
- });
1223
+ mustache__push_text_instruction(&s, s.stack[s.index].data_pos,
1224
+ s.stack[s.index].data_end -
1225
+ s.stack[s.index].data_pos);
953
1226
  s.stack[s.index].data_pos = s.stack[s.index].data_end;
954
1227
  continue;
955
1228
  }
956
1229
  if (beg != start) {
957
1230
  /* there's text before the instruction */
958
- mustache__instruction_push(
959
- &s, (mustache__instruction_s){
960
- .instruction = MUSTACHE_WRITE_TEXT,
961
- .data =
962
- {
963
- .name_pos = s.stack[s.index].data_pos,
964
- .name_len = beg - start,
965
- },
966
- });
1231
+ mustache__push_text_instruction(&s, s.stack[s.index].data_pos,
1232
+ (uint32_t)(uintptr_t)(beg - start));
967
1233
  }
968
1234
  /* move beg (reading position) after the delimiter */
969
1235
  beg += s.stack[s.index].del_start_len;
@@ -974,19 +1240,39 @@ MUSTACHE_FUNC mustache_s *(mustache_load)(mustache_load_args_s args) {
974
1240
  *args.err = MUSTACHE_ERR_CLOSURE_MISMATCH;
975
1241
  goto error;
976
1242
  }
1243
+
977
1244
  /* update reading position in the stack */
978
1245
  s.stack[s.index].data_pos = (end - s.data) + s.stack[s.index].del_end_len;
979
1246
 
1247
+ /* Test for stand-alone tags */
1248
+ if (!end[s.stack[s.index].del_end_len] ||
1249
+ end[s.stack[s.index].del_end_len] == '\n' ||
1250
+ (end[s.stack[s.index].del_end_len] == '\r' &&
1251
+ end[1 + s.stack[s.index].del_end_len] == '\n')) {
1252
+ char *pad = beg - (s.stack[s.index].del_start_len + 1);
1253
+ while (pad >= start && (pad[0] == ' ' || pad[0] == '\t'))
1254
+ --pad;
1255
+ if (pad[0] == '\n' || pad[0] == 0) {
1256
+ /* Yes, this a stand-alone tag, store padding length + flag */
1257
+ ++pad;
1258
+ stand_alone_pos = pad - s.data;
1259
+ stand_alone =
1260
+ ((beg - (pad + s.stack[s.index].del_start_len)) << 1) | 1;
1261
+ }
1262
+ }
1263
+
980
1264
  /* parse instruction content */
981
- uint8_t escape_str = 1;
1265
+ flag = 1;
982
1266
 
983
1267
  switch (beg[0]) {
984
1268
  case '!':
985
- /* comment, do nothing */
1269
+ /* comment, do nothing... almost */
1270
+ mustache__stand_alone_adjust(&s, stand_alone);
986
1271
  break;
987
1272
 
988
1273
  case '=':
989
1274
  /* define new seperators */
1275
+ mustache__stand_alone_adjust(&s, stand_alone);
990
1276
  ++beg;
991
1277
  --end;
992
1278
  if (end[0] != '=') {
@@ -1038,9 +1324,10 @@ MUSTACHE_FUNC mustache_s *(mustache_load)(mustache_load_args_s args) {
1038
1324
 
1039
1325
  case '^': /*overflow*/
1040
1326
  /* start inverted section */
1041
- escape_str = 0;
1327
+ flag = 0;
1042
1328
  case '#':
1043
1329
  /* start section (or inverted section) */
1330
+ mustache__stand_alone_adjust(&s, stand_alone);
1044
1331
  ++beg;
1045
1332
  --end;
1046
1333
  MUSTACHE_IGNORE_WHITESPACE(beg, 1);
@@ -1058,8 +1345,8 @@ MUSTACHE_FUNC mustache_s *(mustache_load)(mustache_load_args_s args) {
1058
1345
  if (mustache__instruction_push(
1059
1346
  &s,
1060
1347
  (mustache__instruction_s){
1061
- .instruction = (escape_str ? MUSTACHE_SECTION_START
1062
- : MUSTACHE_SECTION_START_INV),
1348
+ .instruction = (flag ? MUSTACHE_SECTION_START
1349
+ : MUSTACHE_SECTION_START_INV),
1063
1350
  .data = {
1064
1351
  .name_pos = beg - s.data,
1065
1352
  .name_len = end - beg,
@@ -1071,17 +1358,51 @@ MUSTACHE_FUNC mustache_s *(mustache_load)(mustache_load_args_s args) {
1071
1358
 
1072
1359
  case '>':
1073
1360
  /* partial template - search data for loaded template or load new */
1361
+ mustache__stand_alone_adjust(&s, stand_alone);
1362
+ if ((stand_alone >> 1)) {
1363
+ /* TODO: add padding markers */
1364
+ if (mustache__instruction_push(
1365
+ &s, (mustache__instruction_s){
1366
+ .instruction = MUSTACHE_PADDING_PUSH,
1367
+ .data = {
1368
+ .name_pos = stand_alone_pos,
1369
+ .name_len = (stand_alone >> 1),
1370
+ .end = s.padding,
1371
+ }})) {
1372
+ goto error;
1373
+ }
1374
+ s.padding = s.m->u.read_only.intruction_count - 1;
1375
+ }
1074
1376
  ++beg;
1075
1377
  --end;
1076
1378
  MUSTACHE_IGNORE_WHITESPACE(beg, 1);
1077
1379
  MUSTACHE_IGNORE_WHITESPACE(end, -1);
1078
1380
  ++end;
1079
- if (mustache__load_file(&s, beg, end - beg))
1381
+ ssize_t loaded = mustache__load_file(&s, beg, end - beg);
1382
+ if (loaded == -1)
1080
1383
  goto error;
1384
+ /* Add latest padding section to text */
1385
+ if ((stand_alone >> 1)) {
1386
+ if (loaded)
1387
+ mustache__instruction_push(
1388
+ &s, (mustache__instruction_s){
1389
+ .instruction = MUSTACHE_WRITE_TEXT,
1390
+ .data =
1391
+ {
1392
+ .name_pos = stand_alone_pos,
1393
+ .name_len = (stand_alone >> 1),
1394
+ },
1395
+ });
1396
+ else if (mustache__instruction_push(
1397
+ &s, (mustache__instruction_s){.instruction =
1398
+ MUSTACHE_PADDING_POP}))
1399
+ goto error;
1400
+ }
1081
1401
  break;
1082
1402
 
1083
1403
  case '/':
1084
1404
  /* section end */
1405
+ mustache__stand_alone_adjust(&s, stand_alone);
1085
1406
  ++beg;
1086
1407
  --end;
1087
1408
  MUSTACHE_IGNORE_WHITESPACE(beg, 1);
@@ -1143,7 +1464,7 @@ MUSTACHE_FUNC mustache_s *(mustache_load)(mustache_load_args_s args) {
1143
1464
  /*overflow*/
1144
1465
  case '&': /*overflow*/
1145
1466
  /* unescaped variable data */
1146
- escape_str = 0;
1467
+ flag = 0;
1147
1468
  /* overflow to default */
1148
1469
  case ':': /*overflow*/
1149
1470
  case '<': /*overflow*/
@@ -1155,8 +1476,8 @@ MUSTACHE_FUNC mustache_s *(mustache_load)(mustache_load_args_s args) {
1155
1476
  ++end;
1156
1477
  mustache__instruction_push(
1157
1478
  &s, (mustache__instruction_s){
1158
- .instruction = (escape_str ? MUSTACHE_WRITE_ARG
1159
- : MUSTACHE_WRITE_ARG_UNESCAPED),
1479
+ .instruction = (flag ? MUSTACHE_WRITE_ARG
1480
+ : MUSTACHE_WRITE_ARG_UNESCAPED),
1160
1481
  .data = {.name_pos = beg - s.data, .name_len = end - beg}});
1161
1482
  break;
1162
1483
  }
@@ -1166,12 +1487,36 @@ MUSTACHE_FUNC mustache_s *(mustache_load)(mustache_load_args_s args) {
1166
1487
  *args.err = MUSTACHE_ERR_CLOSURE_MISMATCH;
1167
1488
  goto error;
1168
1489
  }
1169
- /* add instruction closure */
1490
+ /* move padding from section tail to post closure (adjust for padding
1491
+ * changes) */
1492
+ flag = 0;
1493
+ if (s.m->u.read_only.intruction_count &&
1494
+ s.i[s.m->u.read_only.intruction_count - 1].instruction ==
1495
+ MUSTACHE_PADDING_WRITE) {
1496
+ --s.m->u.read_only.intruction_count;
1497
+ flag = 1;
1498
+ }
1499
+ /* mark section length */
1170
1500
  mustache__data_segment_s seg = mustache__data_segment_read(
1171
1501
  (uint8_t *)s.data + s.stack[s.index].data_start);
1172
1502
  s.i[seg.inst_start].data.end = s.m->u.read_only.intruction_count;
1503
+ /* add instruction closure */
1173
1504
  mustache__instruction_push(
1174
1505
  &s, (mustache__instruction_s){.instruction = MUSTACHE_SECTION_END});
1506
+ /* TODO: pop any padding (if exists) */
1507
+ if (s.padding && s.padding + 1 == seg.inst_start) {
1508
+ s.padding = s.i[s.padding].data.end;
1509
+ mustache__instruction_push(&s, (mustache__instruction_s){
1510
+ .instruction = MUSTACHE_PADDING_POP,
1511
+ });
1512
+ }
1513
+ /* complete padding switch*/
1514
+ if (flag) {
1515
+ mustache__instruction_push(&s, (mustache__instruction_s){
1516
+ .instruction = MUSTACHE_PADDING_WRITE,
1517
+ });
1518
+ flag = 0;
1519
+ }
1175
1520
  /* pop stack */
1176
1521
  --s.index;
1177
1522
  }
@@ -1,11 +1,27 @@
1
1
  module Iodine
2
- # Iodine includes a strict and extra safe Mustache templating engine.
2
+ # Iodine includes a safe and fast Mustache templating engine.
3
3
  #
4
- # The Iodine Mustache templating engine provides increased XSS protection through agressive HTML escaping. It's also faster than the original (Ruby based) Mustache templating engine.
4
+ # The engine is also faster and simpler than the official and feature richer Ruby engine.
5
5
  #
6
- # Another difference is that the Iodine Mustache templating engine makes it eady to load the templates from the disk (or specify a virtual filename), allowing for easy patial template path resolution.
6
+ # Note: {Iodine::Mustache} behaves differently than the official Ruby templating engine in a number of ways:
7
7
  #
8
- # There's no monkey-patch for `mustache` Ruby gem since the API is incompatible.
8
+ # * When a partial template can't be found, a `LoadError` exception is raised (the official implementation outputs an empty String).
9
+ #
10
+ # * HTML escaping is more agressive, increasing XSS protection. Read why at: https://wonko.com/post/html-escaping .
11
+ #
12
+ # * Partial template padding in Iodine is adds padding to dynamic text as well as static text. i.e., unlike the official Ruby mustache engine, if an argument contains a new line marker, the new line will be padded to match the partial template padding.
13
+ #
14
+ # * Lambda support is significantly different. For example, the resulting text isn't parsed (no lambda interpolation).
15
+ #
16
+ # * Dot notation is tested in whole as well as in part (i.e. `user.name.first` will be tested as is, than the couplet `"user","name.first"` and than as each `"use","name","first"`), allowing for the Hash data to contain keys with dots while still supporting dot notation shortcuts.
17
+ #
18
+ # * Dot notation supports method names (even chained method names) as long as they don't have or require arguments. For example, `user.class.to_s` will behave differently on Iodine (returns `"String"`) than on the official mustache engine (returns empty string).
19
+ #
20
+ # Iodine Mustache's engine was designed to play best with basic data structures, such as results from the {Iodine::JSON} parser.
21
+ #
22
+ # Hash data is tested for String keys before being tested for Symbol keys and methods. This means that `"key"` has precedence over `:key`.
23
+ #
24
+ # Note: Although using methods as "keys" (or argument names) is supported, no Ruby code is evaluated.
9
25
  #
10
26
  # You can benchmark the Iodine Mustache performance and decide if you wish to switch from the Ruby implementation.
11
27
  #
@@ -1,3 +1,3 @@
1
1
  module Iodine
2
- VERSION = '0.7.12'.freeze
2
+ VERSION = '0.7.13'.freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: iodine
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.12
4
+ version: 0.7.13
5
5
  platform: ruby
6
6
  authors:
7
7
  - Boaz Segev
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-11-30 00:00:00.000000000 Z
11
+ date: 2018-12-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -210,7 +210,7 @@ licenses:
210
210
  - MIT
211
211
  metadata:
212
212
  allowed_push_host: https://rubygems.org
213
- post_install_message: 'Thank you for installing Iodine 0.7.12.
213
+ post_install_message: 'Thank you for installing Iodine 0.7.13.
214
214
 
215
215
  '
216
216
  rdoc_options: []