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 +4 -4
- data/CHANGELOG.md +6 -0
- data/ext/iodine/fio.c +6 -1
- data/ext/iodine/fiobj_mustache.c +4 -89
- data/ext/iodine/iodine_mustache.c +27 -39
- data/ext/iodine/mustache_parser.h +394 -49
- data/lib/iodine/mustache.rb +20 -4
- data/lib/iodine/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 943a41860e82f16cade6bb4f0ba8eabab9d712eb620de04cfbeca7153e4e8551
|
4
|
+
data.tar.gz: ef24434b749862566b869ff248089bc5f420a46b0492644096a40588766d3c3e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 95c9c98a8b85201a98989e96379fbbaeb74e3d16ae9ea4b42bf3fd49d7591d7224bb406d568298b19583411f675b337110d464ad7447221ee19583d10e6a37aa
|
7
|
+
data.tar.gz: 4614ca29b759b2c1c8e6329c38b565415eb69c356298a53429f0af100545fe1944e1340ccf6334e55d444d8f7659c13c3e6d1f45e6b9d384972acbaa59f7f8d4
|
data/CHANGELOG.md
CHANGED
@@ -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.
|
data/ext/iodine/fio.c
CHANGED
@@ -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
|
-
|
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,
|
data/ext/iodine/fiobj_mustache.c
CHANGED
@@ -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] = "<"
|
85
|
-
a['>'.ord] = ">"
|
86
|
-
a['&'.ord] = "&"
|
87
|
-
a['"'.ord] = """
|
88
|
-
a["\'".ord] = "'"
|
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
|
-
"�", "", "", "", "", "", "", "",
|
97
|
-
"", "	", " ", "", "", " ", "", "",
|
98
|
-
"", "", "", "", "", "", "", "",
|
99
|
-
"", "", "", "", "", "", "", "",
|
100
|
-
" ", "!", """, "#", "$", "%", "&", "'",
|
101
|
-
"(", ")", "*", "+", ",", "-", ".", "/",
|
102
|
-
"0", "1", "2", "3", "4", "5", "6", "7",
|
103
|
-
"8", "9", ":", ";", "<", "=", ">", "?",
|
104
|
-
"@", "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", "[", "\", "]", "^", "_",
|
108
|
-
"`", "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", "{", "|", "}", "~", "\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
|
-
|
182
|
-
|
183
|
-
|
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
|
-
|
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
|
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 (
|
210
|
-
|
211
|
-
|
212
|
-
|
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
|
-
|
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
|
-
|
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(
|
350
|
+
rb_raise(rb_eLoadError, "Iodine::Mustache template not found.");
|
363
351
|
break;
|
364
352
|
case MUSTACHE_ERR_FILE_TOO_BIG:
|
365
|
-
rb_raise(
|
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(
|
519
|
+
rb_raise(rb_eLoadError, "Iodine::Mustache template not found.");
|
532
520
|
break;
|
533
521
|
case MUSTACHE_ERR_FILE_TOO_BIG:
|
534
|
-
rb_raise(
|
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
|
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
|
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
|
-
*
|
130
|
-
*
|
133
|
+
* In non-implementation files, don't define the INCLUDE_MUSTACHE_IMPLEMENTATION
|
134
|
+
* macro.
|
131
135
|
*
|
132
|
-
*
|
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
|
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
|
-
|
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] = "<"
|
536
|
+
a['>'.ord] = ">"
|
537
|
+
a['&'.ord] = "&"
|
538
|
+
a['"'.ord] = """
|
539
|
+
a["\'".ord] = "'"
|
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
|
+
"�", "", "", "", "", "", "", "",
|
548
|
+
"", "	", " ", "", "", " ", "", "",
|
549
|
+
"", "", "", "", "", "", "", "",
|
550
|
+
"", "", "", "", "", "", "", "",
|
551
|
+
" ", "!", """, "#", "$", "%", "&", "'",
|
552
|
+
"(", ")", "*", "+", ",", "-", ".", "/",
|
553
|
+
"0", "1", "2", "3", "4", "5", "6", "7",
|
554
|
+
"8", "9", ":", ";", "<", "=", ">", "?",
|
555
|
+
"@", "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", "[", "\", "]", "^", "_",
|
559
|
+
"`", "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", "{", "|", "}", "~", "\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
|
558
|
-
|
559
|
-
|
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
|
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
|
618
|
-
|
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
|
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
|
-
|
944
|
-
|
945
|
-
|
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
|
-
|
959
|
-
|
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
|
-
|
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
|
-
|
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 = (
|
1062
|
-
|
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
|
-
|
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
|
-
|
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 = (
|
1159
|
-
|
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
|
-
/*
|
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
|
}
|
data/lib/iodine/mustache.rb
CHANGED
@@ -1,11 +1,27 @@
|
|
1
1
|
module Iodine
|
2
|
-
# Iodine includes a
|
2
|
+
# Iodine includes a safe and fast Mustache templating engine.
|
3
3
|
#
|
4
|
-
# The
|
4
|
+
# The engine is also faster and simpler than the official and feature richer Ruby engine.
|
5
5
|
#
|
6
|
-
#
|
6
|
+
# Note: {Iodine::Mustache} behaves differently than the official Ruby templating engine in a number of ways:
|
7
7
|
#
|
8
|
-
#
|
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
|
#
|
data/lib/iodine/version.rb
CHANGED
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.
|
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
|
+
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.
|
213
|
+
post_install_message: 'Thank you for installing Iodine 0.7.13.
|
214
214
|
|
215
215
|
'
|
216
216
|
rdoc_options: []
|